-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Preserve unused CSS when nested within used rules #10844
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
Comments
Hey! I don't think we want to treat nesting as semantically different than the equivalent non-nested selectors — unused CSS removal is useful, and expecting people to understand that Svelte interprets one form differently from the other is too big an ask. It would be especially confusing when people refactor between them, and the behaviour changes under their feet. Instead, I think the solution here is to make it easier to author global styles within a component. There's a proposal in #10815 that uses nesting syntax to achieve this — the styles above would become this: -.gridlines {
+.gridlines :global {
line {
stroke-opacity: .5;
}
} Or, more generally: .stuff-in-my-template :global {
.stuff-d3-cares-about {
/* ... */
}
.other-stuff-d3-cares-about {
/* ... */
}
.etc {
/* ... */
}
} |
I don't recall where I previously saw this issue and a potential solution discussed. What if we had a INPUT: /* GOOD: no error */
:local(.seemingly-non-existent) {
color: red;
}
/* GOOD: error */
.actually-non-existent {
color: blue;
} OUTPUT: .seemingly-non-existent.s-r@nd0m {
color: red;
}
<script>
/** @type {'dark' | 'light} */
let theme = 'dark';
</script>
<div class="red {theme}">foo</div>
<style>
.red.light { color: red }
.red.dark { color: maroon }
/* BAD: no error because Svelte doesn't know what `theme` contains */
.actually-non-existent {
color: blue;
}
</style> Now that we're breaking things with v5, what if we validated all styles and didn't bail on dynamic styles given that a /* GOOD: no error */
:local(red.light) { color: red }
:local(.red.dark) { color: maroon }
/* GOOD: error because it's not in markup and not :local */
.actually-non-existent {
color: blue;
} With the recent rise of headless libraries, Svelte bails too often on style validation to be useful. This could be filed as a separate issue, but if we go the EDIT: |
Closing for the reasons Rich gave above - and in v5 we now have the new |
Uh oh!
There was an error while loading. Please reload this page.
Background
This is an idea about how to solve the very common author pain point where CSS that is not actually unused gets removed.
Here is a sampling of closed issues on this:
In many of these the suggestion is to work around it via
:global()
, but of course that does not preserve the scoping that in many of these cases is desirable.I think a lot of what the discourse is missing is that this could easily happen when integrating existing libraries into Svelte.
For example, D3 + Svelte is the current state of the art for high fidelity interactive visualizations in the dataviz community,
but D3 does not actually know about Svelte. Thankfully, it largely works well with Svelte out of the box, except when it doesn’t.
Case in point, the use case that prompted this: I was generating a chart via D3 and wanted to style ticks via CSS. My HTML looked like this:
and the JS that populated the gridlines was:
Then, I tried to do this in my CSS:
Nope, no result. It took some time to realize it was being commented out as "unused".
I even tried a nested version (I'm using Svelte 5):
Nope squared this time 😁
Proposed solution: Take advantage of CSS nesting
The reasoning given in many of these cases is that Svelte cannot apply classes to elements it doesn't know about. Fair.
But what about descendants? E.g. in my case, the
.gridlines
container was in my CSS, andline
was within it.line
did not actually need any classes for scoping, because it's already scoped via its parent.Instead of parsing selectors to figure out which ones can work like this and which ones cannot (which is a can of worms I do not wish on anyone), I propose a much easier solution: we take advantage of nesting. If the parent of a nested set of rules is not unused, then the children are automatically also not unused (essentially wrapped in
:global()
). This way Svelte 5 can provide a workaround for this longstanding issue in an intuitive way, that involves relatively low implementation complexity.The text was updated successfully, but these errors were encountered: