> Back in December, we wrote an article detailing three different options for CSS Nesting.
> Web developers responded to the poll with great clarity. Option 3 won in a landslide.
Yup, pretty much. The only thing I really wish won however was making the beginning "&" always required for nesting. Instead you're able to omit it if there's any other symbol. Example from article:
The position of requiring & was not represented in the poll but seemed really popular in all the comments and seems like it would only make the parser's job easier (both now and in the future of CSS)
Regardless I'm just happy it's finally here! Hard to see the use-cases for sass in 2024 but, like jQuery, it's definitely made its mark on the history of the development of web standards
https://github.com/w3c/csswg-drafts/issues/7834#issuecomment..., on this exact matter (chosen as a convenient starting point, after matters were already settled, but the whole issue is about it). By the time this poll was done, they had already decided not to consider mandatory-&, though I don’t really understand why (it’s pretty obvious to me that dividing into 3a and 3b would have been much more interesting than a couple of the other options which were straw men that no one was capable of taking seriously even if they tried).
https://github.com/w3c/csswg-drafts/issues/7961, about doing away with & in all cases for descendant selector (… which would be terrible for complexity and worst-case performance, and is only being considered because they already made things inconsistent by making & optional in every other case).
(I don’t like where it’s ended up, and consider requiring the & in all cases to be obviously materially superior, for both machine and human handling, and that there’s clear concrete advantage in having the requirement as an actual language rule rather than only a linter-enforced choice. People are far too hung up on exactly Sass syntax.)
> it would only make the parser's job easier
Mind you, it’s not a big difference; it’s just “if there’s no & in the selector, insert one and a descendant combinator at the start” in some shape, which could be as little as half a dozen lines of code net.
I remember the main argument against this is that you can always enforce this with a linter and either way it's not a slower parser, maybe slightly more complex.
Personally I do agree and I like the explicitness of always having the &
Yeah that's fair and I admit don't actually know anything about the parsing implications. I guess really I'm just looking for an excuse to convince my coworkers to adopt this practice without coming off like I'm bikeshedding haha
yes, mixins. even just static sets of declarations would be a great start, e.g., wanting to apply the same set of base rules to both an entity and various classes without error-prone repetition (which you typically separate to have control over cascading and specificity).
css variables in media queries would be helpful too, but mixins to me is the next big step after :has() (oh so useful, come on firefox!) and this nested css syntax. unfortunately, it may be years for us to see it in browsers. there's no consensus yet on mixin syntax or where in the css lifecycle it would be implemented (there are potentially serious performance implications apparently).
One thing worth noting is that & substitutes for `:is(...)` in spirit under the hood, which means there are some significant behavior differences between native CSS nesting and Sass.
Not a criticism at all (the `:is(...)` behavior is a very useful and welcome enhancement to CSS) but a notable difference worth understanding coming from Sass.
In Sciter, 10 years ago, I came up with style sets:
@set Main {
:root { ... } // root element that has this set applied
.bar { ... }
article { ... }
:root > article { ... }
}
main { style-set: Main; } // main element with the set applied
This solution solves two problems:
1. Modular/componentized style definition - same goal as in nesting styles, but without introduction of new syntax constructs.
2. Reduces CSS resolution load. Rules inside the set are scanned only for DOM children that have style set defined.
3. DOM element may have @styleset="url#name" attribute defined - used in components (Web alike components and React alike components)
If syntactic sugar was really just sugar, JS would have sticked to callback functions, and we’d still be inventing abstractions to make our lives easier.
Syntactic sugar, sometimes, is the promise of sunnier mornings and that matters.
"For a Linux user, you can already build such a system yourself quite trivially by getting an FTP account, mounting it locally with curlftpfs, and then using SVN or CVS on the mounted filesystem. From Windows or Mac, this FTP account could be accessed through built-in software."
When you want to add a feature you should ask first if that can be accomplished by existing mechanisms.
And the second, if to go with with the change that affect millions of users, can we solve not just one problem (that already has a solution like LeSS & Co.) but possibly other principal too?
So for #1. If we already have @media sections, why not to use the same notation?
@media name { ...rules... }
and
@set name { ...rules... }
@set can use existing code in parsers. Not just in browsers but in tons of existing tools and editors that do syntax highlighting.
And #2. Style sets solve problem of global CSS namespace pollution.
Consider this rule:
div [name="some"] { ... }
then, while calculating styles of DOM elements, absolutely all N DOM elements need to be checked against this rule. So it is a O(N) complex task. And proposed nested rules thing does not reduce the N, but may make this even worse - more rules will be used.
While in style set solution:
@set ComponentA {
div [name="some"] { ... }
}
that rule will be checked only against children of componentA - the rule is local/scoped to DOM subtree. Still O(n), but n <<< N.
unpopular opinion I'm sure from reading everyone here, but to me nesting css is syntactic poison - or maybe syntactic sucrose or something that gives you cancer you use too much of it?
Anyway programmers given nesting just do it all the time which ends up leading to stuff like
article.news section.preamble h1 and probably a few other selectors after that all nested giving you a wonderful high priority for your style and possible collisions.
Then the same guys who made the problem can't figure out the problem so they figure some highlevel nesting is the solution so there won't be any collisions and overriding of styles.
But this syntax can also be used in minified CSS. I'm sure minifiers will have a fun time with this in 5 years when most of the major browsers support it.
I've seen all sorts of examples of stylesheets that would benefit from this, Tailwind Typography being a good one because it styles all sorts of nested elements but only inside of `.prose`. Eliminating tens or hundreds of `.prose` selectors by wrapping them all in a single `.prose { }` would make it smaller; not sure exactly how much smaller though.
Maybe I am an outlier... but I really don't like nested style documents like those founds with SASS, LESS, PostCSS. When the number of nested selectors becomes too great it can become very difficult to reason about. I would never use this without some kind of lint rule enforcing a maximum depth of selector nesting. I agree with the other commenters ITT taking the position flat CSS looks cleaner than nested documents.
The difficulty you mention is definitely an issue, but the insane repetition required for even a modestly complex piece of UI when using "flat" CSS is worse, in my opinion. I suppose there might even be a slight efficiency gain over the wire once Sass outputs nested CSS by default.
I wrote flat CSS for many years and yeah it's a lot of work but also I find it becomes more difficult to parse visually.
Like when writing HTML or regular code, I find indentation helps me parse and navigate the structure very easily.
There are cases with complex components where it can become a bit complex, but it's trivial to split the tree in multiple parts to reduce the indentation.
I agree: even when I use preprocessors, I rarely nest “normal” selectors. The real win is nesting pseudoselectors (:hover, :focus, etc) and media queries.
I mean, these days almost every JS developer use some kind of linter - why would you not use a linter for CSS? Literally nothing to lose since you can set the rules as relaxed as you want.
PS: I set it to 3 - Using good convention like SuitCSS I have never found I needed more nesting. Usually you think about a problem and find a simpler way to do it.
TLDR If you use any of the good practices for CSS such as BEM or BEM-like you don't have lots of nesting anyway.
And I like how the article mentions as an example `ul article ul` and how to solve it by adding a symbol :is() to be able to nest, and it's literally the one thing you don't want to do anyway, which is a selector like `ul article ul` (as per BEM methodology). Instead `.list .article .article-list` as a veyr poor example doesn´t need the special "fix" the article mentions.
I generally only nest one level down, and at most two. Any more and, as you say, it becomes very convoluted, particularly when it comes to specificity. However, alongside pseudo elements, the killer feature for me is that nesting enables namespacing for components, which (a) leads to much simpler class names that makes CSS much easier to read and (b) stops a great deal of repetition of the class on the wrapper element.
> When the number of nested selectors becomes too great it can become very difficult to reason about.
This is a sign you need to decompose whatever design you are working on into smaller components. If you’re trying to write styles for a whole page at once, you’ll see this problem, but if you are working on small components, it’s very rare.
If you give a junior front end developer CSS nesting, you can get some very janky results.
Working with SCSS codebases and junior developers, I've seen SCSS nesting get so bad, the line order of the selector in the stylesheet would determine what styling gets used.
Nesting CSS is useful in some cases, but for the majority of use cases it can get unwieldy very quickly.
When the number of nested selectors becomes too great it can become very difficult to reason about.
When any complexity becomes too much it makes it harder to reason about. The solution is to avoid complexity; you can do that while using a small number of nested selectors.
It’s Sass & Less, not SASS & LESS. Less is sometimes stylized with all caps (or all lower like {less}) and is an acronym for Leaner Style Sheets, but Sass was never was stylized with all caps nor is it an acronym.
It's great that parts of SCSS are getting superseded by CSS.
Nesting is great to avoid selector repetition.
Custom properties (CSS variables) and CSS functions give us tools to avoid magic numbers and to encode layout relationships.
New sets of selectors and container queries let us decouple and re-use declarations more.
CSS Houdini gives us further power in extending CSS functionality.
Of course there are things that SCSS provides which are unlikely in the scope of future CSS proposals such as datastructures (maps) and loops to generate classes and mixins. But I can already imagine a world were those aren't necessary anymore for many cases.
Reminds me of how Coffeescript died out because its best ideas got integrated into Javascript.
What I want to know is: how the hell did this take so long? Nesting is such an obviously useful feature to have in CSS, and once I'd experienced it in SASS I never wanted to be without it. Why did it take 10+ years for this to be introduced to CSS, especially when other browser-based technologies like JS have evolved enormously in that time?
Awesome to finally have nesting. The way I see it, this took 10 years longer than it should have.
I feel the only big thing missing from the vanilla stack for me right now is a template element that multiple html files can share. Just like how you make a blog header in Jekyll or Hugo and it adds it on all your blog posts get the header. I haven't found an easy way of doing that if I have a bunch of html pages.
That's what an iframe is? I guess there could be a partial element that works like <partial href="header.html" /> but if it's referencing HTML, what about JavaScript or CSS it includes? That's exactly what iframe handles.
We had it briefly in the form of HTML imports, but the powers that be removed it because it’s not powerful enough for JavaScript/web apps development; totally ignoring the fact that the web isn’t just about apps.
Will nested CSS support mixing properties and nested rules in any order, or do all the nested rules have to grouped together at the end? Right now, I frequently write SCSS rules like this:
> Web developers responded to the poll with great clarity. Option 3 won in a landslide.
Yup, pretty much. The only thing I really wish won however was making the beginning "&" always required for nesting. Instead you're able to omit it if there's any other symbol. Example from article:
The position of requiring & was not represented in the poll but seemed really popular in all the comments and seems like it would only make the parser's job easier (both now and in the future of CSS)Regardless I'm just happy it's finally here! Hard to see the use-cases for sass in 2024 but, like jQuery, it's definitely made its mark on the history of the development of web standards
https://twitter.com/LeaVerou/status/1580215877705687040, a poll about this specific matter (with links to other relevant polls which on the surface contradict this one).
https://github.com/w3c/csswg-drafts/issues/7834#issuecomment..., on this exact matter (chosen as a convenient starting point, after matters were already settled, but the whole issue is about it). By the time this poll was done, they had already decided not to consider mandatory-&, though I don’t really understand why (it’s pretty obvious to me that dividing into 3a and 3b would have been much more interesting than a couple of the other options which were straw men that no one was capable of taking seriously even if they tried).
https://github.com/w3c/csswg-drafts/issues/7961, about doing away with & in all cases for descendant selector (… which would be terrible for complexity and worst-case performance, and is only being considered because they already made things inconsistent by making & optional in every other case).
(I don’t like where it’s ended up, and consider requiring the & in all cases to be obviously materially superior, for both machine and human handling, and that there’s clear concrete advantage in having the requirement as an actual language rule rather than only a linter-enforced choice. People are far too hung up on exactly Sass syntax.)
> it would only make the parser's job easier
Mind you, it’s not a big difference; it’s just “if there’s no & in the selector, insert one and a descendant combinator at the start” in some shape, which could be as little as half a dozen lines of code net.
- Easier for human readers (the "&" is an unmistakable visual indicator)
- Easier for human writers (fewer decisions; there's Only One Way To Do It)
- Easier for syntax colouring
Personally I do agree and I like the explicitness of always having the &
Possibly mixins, loops, and variables, which, unlike CSS variables, can be used in media queries.
Please god no. CSS is intended to be completely declarative. Loops and conditional logic were the very worst ideas SASS ever had.
css variables in media queries would be helpful too, but mixins to me is the next big step after :has() (oh so useful, come on firefox!) and this nested css syntax. unfortunately, it may be years for us to see it in browsers. there's no consensus yet on mixin syntax or where in the css lifecycle it would be implemented (there are potentially serious performance implications apparently).
Modules make writing CSS much less of a naming game. Anything you can do to reduce one of the 2 hardest problems in CS is worth it.
Because there was no “none of the above” option, which developers asked for.
Here's one:
In Sass, that would compile to: With native nesting, it effectively compiles to: The Sass version matches this DOM structure: But the native version matches all of these structures: Not a criticism at all (the `:is(...)` behavior is a very useful and welcome enhancement to CSS) but a notable difference worth understanding coming from Sass.I recognize this is a preview and I desperately hope this implementation isn’t kept around and treated as a quirk.
This implementation is extremely unintuitive given their explanation of the expected behavior of CSS Nesting and the & symbol.
To quote:
Their explanation and the actual implementation result in a majorly different CSS selector.The implemented functionality, however useful, makes no sense as a default if one can explicitly use :is to achieve this behavior like below.
The default should behave like they claim it does; simply replace & with the “outside” selector.(We discussed adopting Sass's behavior in <https://github.com/w3c/csswg-drafts/issues/8310#issuecomment...> but ultimately dropped it.)
In Sciter, 10 years ago, I came up with style sets:
This solution solves two problems:1. Modular/componentized style definition - same goal as in nesting styles, but without introduction of new syntax constructs.
2. Reduces CSS resolution load. Rules inside the set are scanned only for DOM children that have style set defined.
3. DOM element may have @styleset="url#name" attribute defined - used in components (Web alike components and React alike components)
Syntactic sugar, sometimes, is the promise of sunnier mornings and that matters.
When you want to add a feature you should ask first if that can be accomplished by existing mechanisms.
And the second, if to go with with the change that affect millions of users, can we solve not just one problem (that already has a solution like LeSS & Co.) but possibly other principal too?
So for #1. If we already have @media sections, why not to use the same notation?
and @set can use existing code in parsers. Not just in browsers but in tons of existing tools and editors that do syntax highlighting.And #2. Style sets solve problem of global CSS namespace pollution.
Consider this rule:
then, while calculating styles of DOM elements, absolutely all N DOM elements need to be checked against this rule. So it is a O(N) complex task. And proposed nested rules thing does not reduce the N, but may make this even worse - more rules will be used.While in style set solution:
that rule will be checked only against children of componentA - the rule is local/scoped to DOM subtree. Still O(n), but n <<< N.Anyway programmers given nesting just do it all the time which ends up leading to stuff like
article.news section.preamble h1 and probably a few other selectors after that all nested giving you a wonderful high priority for your style and possible collisions.
Then the same guys who made the problem can't figure out the problem so they figure some highlevel nesting is the solution so there won't be any collisions and overriding of styles.
But this syntax can also be used in minified CSS. I'm sure minifiers will have a fun time with this in 5 years when most of the major browsers support it.
I've seen all sorts of examples of stylesheets that would benefit from this, Tailwind Typography being a good one because it styles all sorts of nested elements but only inside of `.prose`. Eliminating tens or hundreds of `.prose` selectors by wrapping them all in a single `.prose { }` would make it smaller; not sure exactly how much smaller though.
This sounds like a fun case study.
Like when writing HTML or regular code, I find indentation helps me parse and navigate the structure very easily.
There are cases with complex components where it can become a bit complex, but it's trivial to split the tree in multiple parts to reduce the indentation.
https://stylelint.io/user-guide/rules/max-nesting-depth/
I mean, these days almost every JS developer use some kind of linter - why would you not use a linter for CSS? Literally nothing to lose since you can set the rules as relaxed as you want.
PS: I set it to 3 - Using good convention like SuitCSS I have never found I needed more nesting. Usually you think about a problem and find a simpler way to do it.
https://github.com/suitcss/suit/blob/master/doc/naming-conve...
TLDR If you use any of the good practices for CSS such as BEM or BEM-like you don't have lots of nesting anyway.
And I like how the article mentions as an example `ul article ul` and how to solve it by adding a symbol :is() to be able to nest, and it's literally the one thing you don't want to do anyway, which is a selector like `ul article ul` (as per BEM methodology). Instead `.list .article .article-list` as a veyr poor example doesn´t need the special "fix" the article mentions.
This is a sign you need to decompose whatever design you are working on into smaller components. If you’re trying to write styles for a whole page at once, you’ll see this problem, but if you are working on small components, it’s very rare.
I haven't followed modern CSS development much. Does it have a module system now?
Working with SCSS codebases and junior developers, I've seen SCSS nesting get so bad, the line order of the selector in the stylesheet would determine what styling gets used.
Nesting CSS is useful in some cases, but for the majority of use cases it can get unwieldy very quickly.
When any complexity becomes too much it makes it harder to reason about. The solution is to avoid complexity; you can do that while using a small number of nested selectors.
Nesting is great to avoid selector repetition.
Custom properties (CSS variables) and CSS functions give us tools to avoid magic numbers and to encode layout relationships.
New sets of selectors and container queries let us decouple and re-use declarations more.
CSS Houdini gives us further power in extending CSS functionality.
Of course there are things that SCSS provides which are unlikely in the scope of future CSS proposals such as datastructures (maps) and loops to generate classes and mixins. But I can already imagine a world were those aren't necessary anymore for many cases.
Reminds me of how Coffeescript died out because its best ideas got integrated into Javascript.
What I want to know is: how the hell did this take so long? Nesting is such an obviously useful feature to have in CSS, and once I'd experienced it in SASS I never wanted to be without it. Why did it take 10+ years for this to be introduced to CSS, especially when other browser-based technologies like JS have evolved enormously in that time?
This wins in my opinion because there shouldn’t be any conversion necessary from something like Sass to CSS
I Vaguely recall something similar happening with decorator syntax/how it worked in JS/typescript.
Or do you keep to CSS features that are locked down in terms of spec, just waiting on implementation to roll out?
I feel the only big thing missing from the vanilla stack for me right now is a template element that multiple html files can share. Just like how you make a blog header in Jekyll or Hugo and it adds it on all your blog posts get the header. I haven't found an easy way of doing that if I have a bunch of html pages.
They're tracking work in https://bugzilla.mozilla.org/show_bug.cgi?id=1648037
The reason I ask is because I seem to remember an early proposal not allowing this, but I can't find where I read it.