CSS3 Feedback: Selector Blocks

Published 9 years, 6 months ago selector feedback, selector blocks was the part that really caught my attention.

">

(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.


  1. You hit the nail right on the head. This is what I always wished of CSS. I hate having to write a huge pile of pointless markup just to style a group of elements like that.

  2. […] please make the ability to do this happen. It would make CSS so much more lightweight and easier to write. Share […]

  3. This (and constants) look more like something an author tool should be doing. There’s a small advantage in the download time for the stylesheet with a more compact syntax, but if you lose all that in inefficiency interpreting it, it’s pointless. Remember that the author does her work once, but the CSS processor does it every time the page is displayed.
    The whole proposal needs a “what agent should be doing this work?” review.

  4. Yes, that’s the idea I always dreamt for.
    But I think the syntax for it must be something like that:

    @selector body.home #content .entry {
    h2, h3, h4, h5, h6 {...}
    }

    With that @ rule we can improve readability and maybe backwards-compatibility.

  5. This is something similar to what I have been working with on a Ruby on Rails application. There is a module called Sass where you set up CSS blocks and it will generate the actual CSS when the first person visits your web site and uses the generated file until the sass file gets changed.

    It has been very helpful to create blocks instead of copy/paste what is needed causing possible errors. This also helps a lot for readability because you don’t have to look through long lists of CSS selectors.

  6. I’ve gone so far as to sketch out what it would take to write a pre-processor so I could write my CSS this way and then have it turned into standard CSS, but I haven’t had the tuits to pull it off yet and integrate it into my build environment.

  7. Having just completed some development on a site which required CSS themes for each top-level section, resulting in large blocks of similar rules differing only in the top level class, I love the clarity of this syntax – a kind of JSON-like equivalent of SASS.

    Perhaps this syntax could be formalized and a preprocessor written to produce valid CSS – to be run before deployment as part of a deployment script, or as a TextMate bundle.

  8. Is there any kind of research that shows how this would simplify actual style sheets in use?

  9. (I’m asking since I think that the bar for fundamental syntax changes should be pretty high.)

  10. Nesting blocks sounds very handy–there are a few places where it would really clean up my code. Seeing it laid out here is almost like a slap in the face–“why didn’t I think of that?”

    Parent selectors, difficult or not, would be handy. So would antecedent selectors: we can do a>b right now; I’d like to be able to do a<b as well.

  11. No, Anne, there’s no formalized research, no control groups, no statistics. Please feel free to undertake it if you think it’s necessary. I did provide an example, and I don’t think it’s hard to see how that sort pattern would be very, very widely used and useful.

    From my personal point of view, having worked with CSS for 15 years or so, it seems pretty obvious how this sort of thing would be useful, regardless of how it would change the syntax (which, from an author’s view, is not all that much).

    A lot of other people have arrived at the same conclusion, not by sitting around thinking up weird variants on the existing syntax, but by running into this same problem day in and day out and grumbling, “Man, this would be so much easier if I could just…”

  12. As for the “this should be preprocessed, not interpreted” argument, that works for anyone who has a preprocessor script or tool available. Not everyone does, and not everyone can. Professionals should, yes, but there are a lot more CSS users than just the professionals.

    After all, CSS is already an interpreted language: we don’t have a preprocessor turn our external style sheets into inline style calls, or at least I don’t know anyone who does. This is extra interpretation, it’s true, but so what? The only way to avoid increasing the interpretive load of CSS is to never add anything to it ever again.

    JS engines are getting massively faster. Maybe some (more) effort could be put into doing the same for CSS engines.

  13. Nice idea, I never thought of such a thing. I guess that’s why you are the CSS Guru after all. For some reason I expect there to be a ‘@’ before the selector like – @ .main { body { color: black; } }

  14. Nail on the head. This would make my life so much easier.

  15. Yes… I’ve wanted/needed this many times before, as well as constants. As for a specific use case, take a look at the Dojo stylesheets and themes. The soria.css file has 350 rules, most of which are all prefixed by a .soria class selector.

    I can also see allowing nesting of groups. In the Dojo example, you could have something like (this is extreme… but actual code)


    .soria {
      .digitTab {
        .dijitClosable {
          .closeImage { ... }
          .closeNode { ... }
        }
      }
    }

    I agree that preprocessing is not the way to go. The problem with preprocessing is it breaks the portability of the style language because not everyone is going to like the same style of preprocessing. So… you’d need another standard. We already have one. It just needs tweaking.

    As for it being slow, it should be dead simple to cache the parsed version along with the raw text version. But I can’t really see it being much slower anyway. Add a ‘selector prefix’ string that gets built and prefixed to each selector for each level of grouping. In fact, it might even speed things up since you don’t have to parse the same set of selectors each time… you already have them. Not that I’ve written a CSS parser, but I have written other parsers.

  16. Just a quick clarification on my example… there are lots of other rules under the .digitTab class and the .soria class. So it would actually make more sense than it does as shown.

  17. HTML5 introduces scoped stylesheets which will address your problem. In fact, the example of yours using the style attribute is conceptually the way they work, except that we use the style element instead.

    <div>
    <style scoped>@import url(main-style.css);</style>
    … style applied to everything within this div …
    </div>

    Then in main-style.css, you just use the original CSS without needing to prepend .main to each selector.

  18. The argument for making this interpreted instead of preprocessed is obvious: this will reduce the size of the CSS document (and introduce a new avenue for automated CSS compression), and we should use every method in our power to reduce the filesize of the files we serve to our users.

    The main place this would help me is with sprite-based hover states on links etc.:

    
    li.aboutus a {
        background-position: 0 0;
    }
    
    li.aboutus a:hover,
    li.aboutus a:focus,
    #aboutus li.aboutus a {
        background-position: 0 100%;
    }
    
    

    Times ten navigation items and this function becomes invaluable.
    Now if only Firefox would support background-position-y

  19. […] CSS3 Feedback: Selector Blocks […]

  20. I would like to see an example of a large stylesheet that uses both the new and old syntax.

    I think gzip compression (which if your server isn’t using, it should be) really reduces the worry about markup like this increasing your file size.

    Repetition (like .main appearing 50 times in a file) is really negligible when a file is sent compressed.

    Personally, I think the current syntax is easier to read and maintain, even if when you’re programming it, it seems a bit tiresome.

    CSS has much bigger improvements to be made than catering to programmer preference, like parent/ascendent selectors (come on implementors, jQuery can do it), contains(), etc…

  21. I think both the .main {} and imort has its advantages, would really like to see both in all browsers soon

  22. If we need some kind of @keyword to define a block, @with would make sense.

    @with #content {
      h1, h2, h3, h4, h5, h6 {
        awesomeness: on;
      }
    }

  23. Maybe I’m misreading it, but shouldn’t

    Honestly, it”s pretty much impossible to increase dependence on descendant selectors.

    be

    Honestly, it”s pretty much impossible to decrease dependence on descendant selectors.

    ?

  24. Lachlan, so far as I can tell, that exact same capability exists in the CSS style-attribute module to which I linked, and it has for almost seven years now. What’s the rationale for adding brand-new element capabilities to HTML 5 in this circumstance, when the same thing could be done more cleanly through CSS alone?

  25. I agree with Eric that the HTML 5 scooped style element seems like a bad idea.

    The thing that struck me about it, is that it would work (somewhat) well only for styles that are scooped for certain unique elements. It could do the equivalent of this:

    @with #mydiv {
    .this { … }
    .that { … }
    }

    But it could not do anything like this:

    @with .mykindofdiv {
    .this { … }
    .that { … }
    }

    You’d have to add a scooped style element to every single div with the class “mykindofdiv”. That kind of repetition is not the right thing to do.

  26. Steven, I don’t have time to do it right now, but I’ll try to work on the Dojo Soria stylesheet tonight. That’s 350 rules in 1474 lines and about 41KB.

    But you may be right about the gzip compression. The soria.css file compresses to 6536 bytes from 41374 bytes uncompressed, for 84% compression rate. However, I still perfer the grouping from a readability/maintainability standpoint.

  27. The comments feed comes up empty for me, btw …

  28. Another nice thing would be to take thinks like blueprint classes and equate them to a single class:

    #headline { .span-12, .prepend-5, .append-7, .last, .column }

    Semantics + grid = awesome.

  29. I’m actually not a fan of nesting CSS like that. It doesn’t seem like it would advance CSS at all, just bring up more arguments about structure.

    I’d really rather see CSS variables implemented.

  30. Wirh regard to descendant selectors, I would build on today’s selector syntax:


    body.home #content > .entry > {
    h1, h2, h3, h4, h5, h6 {...}
    }

  31. Just to clarify, when I mentioned preprocessing it was because I wish CSS did this natively, and I was just looking to do it for myself as I didn’t know if it would ever happen in the standard. The less preprocessing the better, I think.

  32. I sometimes have to work with third party controls that inject their own styles, but don’t take enough care to make sure they aren’t inheriting something that will cause the whole thing to fall apart.

    More then once I’ve copied the entire contents of reset.css and replaced each selector with some prefix rule, and I really like the idea of being able to do:

    #someContainer {@import url(reset.css);}

  33. […] Eric’s Archived Thoughts: CSS3 Feedback: Selector Blocks […]

  34. Real shame about the inability to implement parent selectors. The classic use case is indeed images that have a link round them, but you don’t want underlining.

    Perhaps Eric’s proposal for any-element linking would remove that use case?

  35. As others have mentioned, SASS supports something very similar to this, and ever since I’ve switched to using it I’ve noticed a large increase in my productivity in creating complex, manageable styles. What’s best is that with this feature, CSS becomes much more refactor-able – you can move blocks around or re-parent them with very little effort. Knocking out the redundancy is key in that situation. Honestly, there are projects I’ve worked on where having this one feature has saved huge amounts of time and made the stylesheets immensely easier to read.

    SASS also adds a nice bit, the “&” symbol, which lets you reference the parent selector when defining inner selectors, which can be useful for things like pseudo-selectors and browser-specific scoping:

    #content {
      width: 30px;
      .msie & {
        width: 35px;
      }
      &:hover {
        color: blue;
      }
    }

    expands to:

    #content {
      width: 30px;
    }
    .msie #content {
      width: 35px;
    }
    #content:hover {
      color: blue;
    }

  36. Here’s an idea, how about we use a pre-processor in addition to adding support for the new CSS syntax.

    example:
    link rel=sass href=layout.sass
    link rel=stylesheet href=layout.css

    The layout.css would be generated from the layout.sass and used by current browsers. Newer browsers that can handle SASS, when they came across sites with SASS would use that file and disregard the css file.

    This way, the new syntax gets standardised instead of 10 different preprecessors, and we can start using it immediately, and don’t have to wait all browsers catch up.

    Advantage for us (designers & developers), we get something standardized that we can start using right away, that will save time and make maintenance so much easier.

    Advantage for users, smaller files.

  37. I would have loved to have this when we redesigned our company website – we had a handful of colors that are randomly used on each page, and if you add page specific styles on top of that you end up with a loooot of different color + page + container + element scenarios that turned into pages of repeating CSS rules.

    Instead of having to handle one huge unruly CSS file I sprinkled some utility classes in my templates instead. Presentational hooks, but what can you do? Ease of maintenance was more important.

    And yes, I know I’m late to join the party :)

  38. Hey Erice – care to print this out and fax it to the W3C? Surely, they have a fax machine by now ;-)

    http://lesscss.org/

Leave a Comment

Management reserves the right to edit or remove any comment, especially when abusive or irrelevant to the topic at hand. HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <em> <i> <q cite=""> <s> <strong> <pre class=""> <kbd>


Comment Preview

If you're satisfied with what you've written, then go ahead...