meyerweb.com

Skip to: site navigation/presentation
Skip to: Thoughts From Eric

CSS3 Feedback: Selector Blocks

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

38 Responses»

    • #1
    • Comment
    • Thu 12 Feb 2009
    • 1043
    behe wrote in to say...

    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
    • Pingback
    • Thu 12 Feb 2009
    • 1057
    Received from CSS3 Feedback: Selector Blocks | Trevor Davis

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

    • #3
    • Comment
    • Thu 12 Feb 2009
    • 1115
    John wrote in to say...

    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
    • Comment
    • Thu 12 Feb 2009
    • 1116
    kizu wrote in to say...

    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
    • Comment
    • Thu 12 Feb 2009
    • 1135
    Andrew Assarattanakul wrote in to say...

    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
    • Comment
    • Thu 12 Feb 2009
    • 1139
    Stephen Howard wrote in to say...

    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
    • Comment
    • Thu 12 Feb 2009
    • 1209
    Chris Platts wrote in to say...

    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
    • Comment
    • Thu 12 Feb 2009
    • 1211
    Anne van Kesteren wrote in to say...

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

    • #9
    • Comment
    • Thu 12 Feb 2009
    • 1212
    Anne van Kesteren wrote in to say...

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

    • #10
    • Comment
    • Thu 12 Feb 2009
    • 1242
    Adam Rice wrote in to say...

    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
    • Comment
    • Thu 12 Feb 2009
    • 1337
    Eric Meyer wrote in to say...

    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
    • Comment
    • Thu 12 Feb 2009
    • 1342
    Eric Meyer wrote in to say...

    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
    • Comment
    • Thu 12 Feb 2009
    • 1433
    Neal G wrote in to say...

    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
    • Comment
    • Thu 12 Feb 2009
    • 1659
    Matt Wilcox wrote in to say...

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

    • #15
    • Comment
    • Thu 12 Feb 2009
    • 1659
    Michael Johnson wrote in to say...

    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
    • Comment
    • Thu 12 Feb 2009
    • 1702
    Michael Johnson wrote in to say...

    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
    • Comment
    • Thu 12 Feb 2009
    • 1908
    Lachlan Hunt wrote in to say...

    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
    • Comment
    • Thu 12 Feb 2009
    • 1935
    Kit Grose wrote in to say...

    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
    • Pingback
    • Thu 12 Feb 2009
    • 2257
    Received from Bruno Campagnolo de Paula weblog » Resumo do dia para 2009-02-12

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

    • #20
    • Comment
    • Fri 13 Feb 2009
    • 0101
    Steven wrote in to say...

    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
    • Comment
    • Fri 13 Feb 2009
    • 0506
    Andreas wrote in to say...

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

    • #22
    • Comment
    • Fri 13 Feb 2009
    • 0736
    Jake Archibald wrote in to say...

    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
    • Comment
    • Fri 13 Feb 2009
    • 0816
    Five Minute Argument wrote in to say...

    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
    • Comment
    • Fri 13 Feb 2009
    • 0838
    Eric Meyer wrote in to say...

    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
    • Comment
    • Fri 13 Feb 2009
    • 0915
    Bertilo Wennergren wrote in to say...

    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
    • Comment
    • Fri 13 Feb 2009
    • 0953
    Michael Johnson wrote in to say...

    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
    • Comment
    • Fri 13 Feb 2009
    • 1055
    Steven wrote in to say...

    The comments feed comes up empty for me, btw …

    • #28
    • Comment
    • Sun 15 Feb 2009
    • 1137
    Jemaleddin wrote in to say...

    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
    • Comment
    • Sun 15 Feb 2009
    • 1143
    Tim Wright wrote in to say...

    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
    • Comment
    • Mon 16 Feb 2009
    • 0923
    Erik Mogensen wrote in to say...

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


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

    • #31
    • Comment
    • Mon 16 Feb 2009
    • 1312
    Stephen Howard wrote in to say...

    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
    • Comment
    • Mon 16 Feb 2009
    • 1444
    Jason Penney wrote in to say...

    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
    • Pingback
    • Tue 17 Feb 2009
    • 0300
    Received from Max Design - standards based web design, development and training » Some links for light reading (17/2/09)

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

    • #34
    • Comment
    • Tue 17 Feb 2009
    • 0918
    Thomas Tallyce wrote in to say...

    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
    • Comment
    • Wed 18 Feb 2009
    • 0059
    Ben Hollis wrote in to say...

    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
    • Comment
    • Fri 20 Feb 2009
    • 1501
    Arpan wrote in to say...

    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
    • Comment
    • Wed 11 Mar 2009
    • 0549
    Erik wrote in to say...

    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
    • Comment
    • Mon 29 Jun 2009
    • 1000
    Erling Ormar Vignisson wrote in to say...

    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

Line and paragraph breaks automatic, e-mail address required but never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>



Remember to encode character entities if you're posting markup examples! Management reserves the right to edit or remove any comment—especially those that are abusive, irrelevant to the topic at hand, or made by anonymous posters—although honestly, most edits are a matter of fixing mangled markup. Thus the note about encoding your entities. If you're satisfied with what you've written, then go ahead...


February 2009
SMTWTFS
January March
1234567
891011121314
15161718192021
22232425262728

Sidestep

Feeds

Extras