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.
Contemporary CSS engines use bucketing, so a rule like div[name="some"] will only ever be considered for something that has (depending on your browser implementation) either a “name” attribute or is a <div>. In other words, “Absolutely all N DOM elements need to be checked” is completely wrong. It's worst-case O(n), sure, but on a practical page nowhere near average-case O(n).
As for @media queries, I can't say anything about Gecko or WebKit, but at least in Blink they're handled entirely different. And @scope (which is the closest I know of to your hypothetical @set) is significantly less efficient than bucketing is, or even a simple parent selector, since it needs to walk up the DOM after the rule matched.
In worst case complexity of styles resolution is O(nS*nD) where nS is a number of style rules and nD is a number of DOM elements. That's for current flat style table used by CSS. You can optimize bits and cases but it in general problem is like that.
> @media queries ... at least in Blink they're handled entirely different.
@media and @set are different mechanisms. :root in @set matches element that has this set applied. @set's are scoped set of rules. @media are not.
But I was talking about different thing - parsing, in particular in syntax highlighters and other tools.
@set aside-set {
:root {...}
... nA more rules
}
@set main-set {
:root {...}
... nM more rules
}
<aside> content style resolution will always be constant and independent from <main>, no matter how many nM rules you will have. While currently (with the flat table) adding rules for <main> content will increase complexity of <aside> DOM resolution too.
As a bonus: rules in two sets are completely isolated. Adding rule in main-set will never break existing aside design.
> In worst case complexity of styles resolution is O(nS*nD) where nS is a number of style rules and nD is a number of DOM elements. That's for current flat style table used by CSS. You can optimize bits and cases but it in general problem is like that.
Worst-case isn't really interesting for browsers, though. CSS doesn't have an adversarial performance model.
> @media and @set are different mechanisms. :root in @set matches element that has this set applied. @set's are scoped set of rules. @media are not.
Again, this roughly matches @scope (where :scope matches the element that has the given selector applied). But this is just a fantasy spec, right? There are no browsers that implement this, and just like with @scope or nesting or CSS at all, there's no guarantee in the spec what performance characteristics you would have?
FWIW, if you want something like this and get it through CSSWG, you'll most likely see it end up with worse performance than flat CSS, unless it becomes _really_ popular and browsers see the need to optimize certain sub-cases of it (the general case, with tons of different sets, will be nearly impossible to make really fast).
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.
I’ve seen this plenty of times in CSS codebases. Nesting is a write-time optimisation but a read-time hinderance.
If users stick to nesting for lightweight scoping, things will go well. But they wont: they’ll use it to DRY every possible line and their inheritors will suffer dehydration.
not just read-time, but yes it is that as well, I find a class I search for this class, now I need to understand all scope in html tree and scope of the css to understand am I looking at the right one (sure you can run into situations with that as well, but in my experience nesting makes it more difficult)
but it is also a source of bugs, because complicated selectors mess with css priority and the cascade then someone writes something that has side effects and blame CSS instead of their ridiculously long nesting.
Well yes, nesting is inherently a concretion that can lead to overly specific selectors. One of our/my style guide rules is to avoid nesting (in SCSS) when it's unnecessary. When nesting is used it should read "this declaration only makes sense in this nested context".
But not having nesting at all is similarly problematic as having too much nesting, because it can lead to repetition, proliferation of classes and brittleness.
I worked in several codebase that used SCSS and due to those little hacks popping up in review, every time we ended up putting a "rule" in place to stick to two levels of nesting at most, or 3 if you're using the third nesting-level for pseudo-selectors (:hover :before etc). And this rule very rarely had to be broken, as it was extremely easy to avoid being too clever with nesting. (Of course, if there was a real good technical reason, we'd "break" it too).
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.
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)