Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A way to declare a scoped class for the CSS compiler (ex use:class) #8345

Closed
adiguba opened this issue Mar 2, 2023 · 5 comments
Closed

A way to declare a scoped class for the CSS compiler (ex use:class) #8345

adiguba opened this issue Mar 2, 2023 · 5 comments

Comments

@adiguba
Copy link
Contributor

adiguba commented Mar 2, 2023

Describe the problem

Svelte's CSS scoping is great and allow to avoids a lot of mistakes/conflicts.

But it is logically limited to the class attribute and the class: directive.
Obviously the CSS compiler can't detect classes defined outside of these attribute/directive...

It cannot detect:

  • The classes set inside an use:action.
  • The classes set via the DOM (for ex. via an external library).

And in this case, we got the famous warning "Unused CSS selector" :

<div class="btn" use:anActionThatCanAddClasses>
	...
</div>

<style>
	.btn {
		background: blue;
	}

	/* WARNING : Unused CSS selector */
	.important { 
		background: red;
		border: 3px solid black;
	}
</style>

The actual solution is to use the :global(...) modifier, but if it's misused it can break the scoping and have non-intuitive behavior on the selector specificity.

Example :

	.btn {
		background: blue;
	}

	/* NO warning, but
		- style is not scoped, and can affect other nodes
		- some properties are not applied, as the class ".important"
		  is less relevant than the ".btn.svelte-XXXX" scoped CSS.
 	*/
	:global(.important) { 
		background: red; /* NOT applied */
		border: 3px solid black;
	}

The correct way to do this is to bundle the scoped and global CSS , like this :

	.btn:global(.important) { 
		...
	}

But it's verbose and not intuitive.

Describe the proposed solution

It should be possible to declare classes that "could" be added to a node.

This would only serve the CSS compiler and would have no impact on the generated JavaScript code.

I think we could give a special meaning to the use:class="names" directive.
It's a non-breaking change as class is a keyword, and so it cannot be used as an action.

So use:class="names" could be a special case, which would expect a string containing the class names (like a normal class attribute).

Ex:

<div class="btn" use:class="important"> ... </div>

Which means that this <div> only has the "btn" class, but it can have the "important" class.

The CSS compiler would use this in addition to the class attribute/directive to determine the scope of CSS rules, which will avoids the need of :global.

Example :

<div class="btn" use:class="important" use:anActionThatCanAddClasses>
	...
</div>

<style>
	.btn {
		background: blue;
	}
	/* It just work ! */
	.important { 
		background: red;
		border: 3px solid black;
	}
</style>

Alternatives considered

continue to use :global()

Importance

nice to have

@emmbm
Copy link

emmbm commented Mar 2, 2023

Although this doesn't deal with consumer/children components, it sounds quite familiar to one of the long standing debates in svelte discussed here: #2870.

Ultimately, i'd boil it down to "Who should have access to a component's scoping class and what api should provide access to it outside compile-time?". If there ever is a way to access scoped classes through directives, should or shouldn't the same apply to component props (explicit or implicit is left tbd)?

@adiguba
Copy link
Contributor Author

adiguba commented Mar 6, 2023

I have make a small prototype, available here : https://github.com/adiguba/svelte/tree/use-class

Work pretty well with npm run check an npm run build , but VS Code still mark use:class="x" as error.

@censys-git
Copy link

This simple template causes the issue identified here and in so many other related issues: {@html myvar}. If you pass in various optional HTML, say from CMS, where there are known classes allowed for styling, you define them at this scope. However, they get removed because they are not specifically visible in the template when compiled.

Simply, I think it is great to warn and display the warnings, but not necessary to go to the extent of cleaning it up (removing). That should be the QC part of development, not the framework itself. It's a frustrating part of this framework which I think is otherwise great. Using :global or some other solution just pollutes the global space with unnecessary non-sense. It also gets quite confusing when you go back to it months later.

The solutions seem to just complicate it even more. Why should a framework decide what you include or not include? Love the warnings so I can go clean up my development code once I'm done experimenting and messing around. Hate that it decides to do it for me. That's my decision not the frameworks.
My two cents...

@adiguba
Copy link
Contributor Author

adiguba commented Mar 20, 2023

The purpose of this issue is not to question the utility of scoped classes.
Personally i think that it's a must-have !

The interest of scoped classes is to handle the style of your component, and not the one from an external HTML...

In your case (or if you don't want it), you can simply import a separate CSS file :

<script>
    import "./style.css";

    let myvar = ...
</script>

{@html myvar}

@adiguba
Copy link
Contributor Author

adiguba commented Dec 4, 2023

I just found a fucking simple solution for this problem, and it's already possible event with Svelte 4.

=> Just use a class:name directive with a falsy value.
The style will not be added to the element, but the CSS declaration will not be removed from build, and it will be correctly scoped.

<div class="box" class:red-box={0}>
	Hello World !
</div>

<style>
	.box {
		padding: 8px;
	}

	/* No "Unused CSS selector" warning, and CSS is not removed */
	.red-box {
		background: red;
		color: #fff;
	}

	/* Unused CSS selector, AND CSS is removed from build */
	.blue-box {
		background: blue;
		color: #fff;
	}
</style>

So I close this issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants