CSS3 Feedback: Selector Blocks
Published 15 years, 9 months past(This is part of the Feedback on ‘WaSP Community CSS3 Feedback 2008’ series.)
Out of all the selector feedback, selector blocks was the part that really caught my attention. I also see the usefulness of a parent selector, but that one has come up many times before, and it’s always died at the doorstep of implementors, who point out that it’s far too difficult to implement without serious performance penalties and even risk of browser lockup. See, for example, the comment left by David Hyatt on Shaun Inman’s post on the idea. Similarly, I think constants (or macros or whatever they’re called) are a great idea and would be very helpful, especially if you’re coding up a Jason Special. Both are loaded with use cases, so I don’t feel like I can add a lot; besides, constants are already in the WG charter, so there’s once more hope in the land.
So anyway, selector blocks. To pick one recent example, while working on a project that should very soon see the light of day, I had a situation involving the following chunk of rules.
h1, h2, h3, h4, h5, h6, table { font: 1em Arial, "Helvetica Neue", Helvetica, sans-serif;} h1 {font-size: 275%;} h3:first-child {margin-top: 1em;} p.tagline {margin: -0.25em 0 1.25em; font-size: 125%; color: #7B7960;} h3 {margin: 1.5em 0 0.25em; font-size: 125%;} h3:before {font-size: 75%; counter-increment: subhead;} h4 {margin: 2.5em 0 0.75em; text-transform: uppercase; font-size: 125%; color: #928F59;} p {margin: 0 0 1em;} ul {padding-left: 1.5em;} ul li {list-style: disc; margin: 0.5em 0;}
Nothing unusual about them, of course, unless you count my use of counters. These rules had been written early on in development, and the design had evolved around that part of the document. As more page components were added, I realized that I needed to scope all these rules to one section of the document: specifically, a div
with a class
of main
. So here’s what I had to do.
.main h1, .main h2, .main h3, .main h4, .main h5, .main h6, .main table { font: 1em Arial, "Helvetica Neue", Helvetica, sans-serif;} .main h1 {font-size: 275%;} .main h3:first-child {margin-top: 1em;} .main p.tagline {margin: -0.25em 0 1.25em; font-size: 125%; color: #7B7960;} .main h3 {margin: 1.5em 0 0.25em; font-size: 125%;} .main h3:before {font-size: 75%; counter-increment: subhead;} .main h4 {margin: 2.5em 0 0.75em; text-transform: uppercase; font-size: 125%; color: #928F59;} .main p {margin: 0 0 1em;} .main ul {padding-left: 1.5em;} .main ul li {list-style: disc; margin: 0.5em 0;}
This, on the other hand, is what I really wanted to do:
.main { h1, h2, h3, h4, h5, h6, table { font: 1em Arial, "Helvetica Neue", Helvetica, sans-serif;} h1 {font-size: 275%;} h3:first-child {margin-top: 1em;} p.tagline {margin: -0.25em 0 1.25em; font-size: 125%; color: #7B7960;} h3 {margin: 1.5em 0 0.25em; font-size: 125%;} h3:before {font-size: 75%; counter-increment: subhead;} h4 {margin: 2.5em 0 0.75em; text-transform: uppercase; font-size: 125%; color: #928F59;} p {margin: 1em 0;} ul {padding-left: 1.5em;} ul li {list-style: disc; margin: 0.5em 0;} }
Or, if necessary, to put the whole original chunk into its own style sheet and then do one of the following:
div.main {@import url(main-style.css);} <div class="main" style="@import url(main-style.css);">
Interestingly, the latter is theoretically possible, thanks to the more advanced profiles in the CSS module “Syntax of CSS rules in HTML’s ‘style’ attribute“. I’m not aware of the former having been seriously considered (despite my best efforts, once upon a time), though it’s always possible I missed something.
But either one of those approaches would be a last resort, in my opinion. I’d much rather just wrap the whole chunk in .main { }
, like I showed previously, and be done with it. That capability would also simplify certain annoyingly repetitive patterns, like the very first of those rules. I think it’s pretty obvious which of the following is easier to write and maintain:
body.home #content .entry h2, body.home #content .entry h3, body.home #content .entry h4, body.home #content .entry h5, body.home #content .entry h6 {...} body.home #content .entry { h2, h3, h4, h5, h6 {...} }
I mean, just look at the former, and imagine what one goes through to write it in the first place. Copy, paste, paste, paste, paste, paste… maddening. And that’s just for a small block of CSS like this one. Imagine the tedium of doing this for a block of 50 rules, or 150. (Also, this is the the same thing that was requested in the feedback as “Grouped Alternates“, albeit with a different syntax.)
One objection to this sort of pattern is that it increases dependence on descendant selectors, which are computationally inefficient. But it doesn’t: I had to create a whole bunch of descendant selectors as it was, and did so far more clumsily. And had I missed a command-V somewhere, I’d have had styles that applied outside their intended subtree. Introducing a way to nest blocks like this doesn’t change anything except make it easier and more maintainable to do what we already do. Honestly, it’s pretty much impossible to increase dependence on descendant selectors. The best we can do now is make them less difficult to write.
I realize that the syntax I depict would cause backwards-compatibility problems, as in older browsers would not behave as intended when exposed to this sort of thing, but I’ve also stopped being concerned about that. We can’t keep holding ourselves hostage to decisions made a decade or more back. Provide the capability and authors will use it when they can. Over time, its use will become more prevalent—kind of the same way authors adopted CSS in the first place.
I also realize that this case has been made time and again by many, many other people. This isn’t even the first time I’ve made this case, though I think the other times were within the WG and therefore off the public record. The fact that it keeps being made is a strong indicator that the need exists, and dismissing the idea because the same end effect can be achieved with existing selector syntax (as shown above) isn’t acceptable. That’s like saying that complex selection effects can be achieved with JavaScript or XPath, so there’s no need for advanced CSS selectors.
So that’s my use case. I actually have a bunch more, but they all follow the same basic pattern: the desire to group rules together into subsections of a document, or to otherwise simplify the writing of CSS.