- From: Tab Atkins Jr. <jackalmage@gmail.com>
- Date: Mon, 18 Oct 2010 15:46:00 -0700
- To: www-style list <www-style@w3.org>
CSS preprocessors like SASS and LESS are getting more and more attention, because they simply make the authoring experience better in several ways. They offer Variables and Mix-ins, for example, which we already want to move on (and have for some time in the case of variables, but dropped the ball on). One particular ability that both of those frameworks have that people continually ask me for, though, is declaration block nesting. That is, the ability to do this: #header { prop: value; img { prop: value; } > nav { prop: value; > ul { prop: value; > li { prop: value; > a { prop: value; } } } } } ...and have it be equivalent to: #header { prop: value; } #header img { prop: value; } #header > nav { prop: value; } #header > nav > ul { prop: value; } #header > nav > ul > li { prop: value; } #header > nav > ul > li > a { prop: value; } This is sorta an extreme example, but the ability to nest selectors does indeed make your code more readable. I know that it's a relatively common practice to indent selectors to match the implicit nesting, like this: #header { prop: value; } #header img { prop: value; } #header > nav { prop: value; } #header > nav > ul { prop: value; } #header > nav > ul > li { prop: value; } #header > nav > ul > li > a { prop: value; } Nesting thus has several authoring advantages: 1) It makes it somewhat easier to read the stylesheet, if done well. You don't have to constantly reparse each selector to ensure that it's just selecting a descendant of the previous one. 2) It makes the stylesheet as a whole shorter without loss of readability. Less typing is good, shorter content is good. 3) It makes editting much easier. If the page structure changes (say #header has its id changed), right now you have to change a *lot* of selectors. With nesting, you just change one. Of course, CSS has a big problem with selector nesting like in my first example - it breaks the core grammar pretty badly. (It might also require arbitrary lookahead, but I'm not sure of that.) Discussing this around the team recently, though, we hit on a possible solution - @-rules. I believe that nesting @-rules in a decl block is officially against the core grammar right now, but we discussed this back in the April ftf and kinda-sorta agreed to change this, with the recommendation that @-rules in decl blocks be put at the end, where it would break in a controllable way. Using @-rules, you could introduce nesting with only the minimal grammar breaking we talked about. It would look something like this: #header { prop: value; @nest(img) { prop: value; } @nest(> nav) { prop: value; @nest(> ul) { prop: value; @nest(> li) { prop: value; @nest(> a) { prop: value; } } } } } Another similar case is when you don't want to select descendants/siblings, but just specialize the current selector. For example, take this code: body > article.post > form input[type=checkbox] { prop: value; } body > article.post > form input[type=checkbox]:checked { prop: value; } The only difference between the two selected is the :checked specializing the last part, but you have to repeat the entire long selector, and change both of them if the structure changes. This could potentially be changed to something like: body > article.post > form input[type=checkbox] { prop: value; @this(:checked) { prop: value; } } Another large case for this is doing ::before/::after or other pseudoelements of an element that you are already styling. Same thing: my > long > selector > string { prop: value; } my > long > selector > string::before { prop: value; } is replaced by: my > long > selector > string { prop: value; @this(::before) { prop: value; } } I find the latter much more readable, and definitely more maintainable. Thoughts? ~TJ
Received on Monday, 18 October 2010 22:47:11 UTC