Shoot to kill; CSS selector intent

One type of CSS in particular makes me cringe every time I see it; poor selector intent. Poor selector intent means carpet bombed selectors whose key selector has way too broad a reach; a selector like .header ul{} as opposed to one like .main-nav{}, .widget h2{} instead of .widget-title, article > p:first-child{} as opposed to .intro{}. Selectors whose intent isn’t specific enough.

It’s worth noting that selector intent is something I completely made up at some point today; if you think there’s a better name then please let me know!

Let’s take a closer look at the .header ul{} example. Let’s imagine that ul is indeed the main navigation for our website. It lives in the header, as you might expect, and is currently the only ul in there; .header ul{} is fine, right? Not really. I mean sure, it might work, but it’s not very good. It’s not very future proof and certainly not explicit enough. As soon as we add another ul to that header it will adopt the styling of our main nav and the the chances are we won’t want it to. This means we either have to refactor a lot of code or undo a lot of styling on subsequent uls in that .header to remove the effects of the far reaching selector.

Your selector’s intent must match that of your reason for styling something; ask yourself ‘am I selecting this because it’s a ul inside of .header or because it is my site’s main nav?’. The answer to this question will determine your selector.

It’s all about the key selector…

What determines the impact of a selector is its key selector. The key selector is a very important thing in the world of CSS as browsers read selectors right to left. This means the key selector is the last one before the opening {, for example:

.header ul      { /* ‘ul’ is the key selector */ }
.ul li a        { /* ‘a’ is the key selector */ }
p:last-child    { /* ‘:last-child’ is the key selector */ }

As I discuss in Writing efficient CSS selectors, the key selector plays a big part in CSS efficiency, so it’s worth bearing that in mind, but where selector intent is concerned this is basically the place you need to be looking to see how far reaching your selector is. html > body > section.content > article span{} is a ridiculously convoluted and terrible selector that no one anywhere would ever write (right?) but despite how specific and disastrously over the top it is, its key selector (span) is still very, very broad. It doesn’t matter so much what comes before your key selector, it’s only the key that really matters.

As a general rule you should try and avoid any key selector that is a type selector (basically an element, like ul or span or whatever) or a base object (e.g. .nav or .media). Just because something is the only .media object in your content area it doesn’t mean it always will be.

Let’s keep looking at the .header ul{} example. Let’s assume our markup is thus, as we’re using the nav abstraction:

<div class=header>

    <ul class=nav>
        [links]
    </ul>

</div>

We could select this in one of several ways:

.header ul{
    [main nav styles]
}

This is bad because as soon as we add any other ul to our header it will look like our main nav. This is dangerous but thankfully easily avoidable.

Secondly we could use:

.header .nav{
    [main nav styles]
}

This is marginally better than the .header ul example, but barely so. We can now safely add another ul without risk, but we can’t add anything else with a .nav class; that means adding sub-navs or breadcrumbs will be a nightmare!

Finally, our best solution would be to add a second class to the ul; a class of .main-nav:

<div class=header>

    <ul class="nav main-nav">
        [links]
    </ul>

</div>

.main-nav{
    [main nav styles]
}

This is good selector intent; we are selecting this element now for exactly the right reasons, not coincidental/circumstantial ones. Now we can add as many more uls and .navs to that .header and the scope of our main nav styles will never reach anything else. We’re no longer carpet bombing!

Keep your key selector as explicit and specific as you possibly can, preferring for it to be a class over anything else. Applying specific styling through a vague selector is dangerous. A generic selector should always carry generic styling and if you want to target something in particular you should perhaps add a class to it. Specific intent requires a specific selector.

Real-life example

A really good example of where I messed up on this myself is on a project I did at Sky; I had a selector which was simply #content table{}. (Eww, I even used an ID!!!) This is a troublesome selector for three reasons; firstly it uses an ID which is a big no, secondly it has a lot higher specificity than it needs to and lastly—and most importantly—it has a poor selector intent. I wasn’t wanting to style these tables because they were in #content, that was just how the DOM landed so that’s how I chose to target them. Entirely my bad.

For the first few weeks this was fine but then all of a sudden we needed to add some tables inside #content that didn’t want to look anything like the previous ones. Uh oh. My previous selector was far too far reaching, I was now having to undo a blanket style I’d set on every table in the #content div. If I’d had a better selector intent then instead of:

#content table{
    [foo styles]
}
#content .bar{
    [undoing foo styles]
    [bar styles]
}

I should/would have had:

.foo{
    [foo styles]
}
.bar{
    [bar styles]
}

And a lot less headaches. By thinking ahead and having a lot more considered selector intent then I would have had a much easier time…

Exceptions

Of course there are always exceptions. It’s perfectly reasonable to have selectors like .main-nav > li where your key selector is a type selector. It also makes perfect sense to target every a inside something like this:

html{
    color:#333;
    background-color:#fff;
}

/* Inverted colour scheme for promotional items. */
.promo{
    color:#fff;
    background-color:#333;
}
    .promo a{
        color:#fff;
        text-decoration:underline;
    }

That is a reasonably sensible far-reaching selector where it does make sense to style every a in a pretty carpet bombed manner.

Final word

In general, instead of carpet bombing your elements, shoot to kill; target them specifically and explicitly. Make sure your selector intent is accurate and targeted.

Think more carefully about why you want to target something and pick a more explicit and sensible selector; refine your selector intent. Do you mean:

.header em{}

or do you really mean:

.tagline{}

Do you want:

.footer p{}

or do you really want:

.copyright{}

Is it wise to select:

.sidebar form{}

or would a safer bet be:

.search-form{}

Consider your CSS selectors’ intent; are you being specific enough? Are your selectors matching things for the right reasons, or is it just happy circumstance? Shoot to kill. Be a CSS sniper, not a CSS carpet bomber.

Incidentally, opting to switch out a longer selector like .header ul for something like .main-nav will also help reduce specificity and increase selector efficiency; win-win-win!

It is also worth noting that Jonathan Snook wrote something similar called the depth of applicability

By Harry Roberts on Tuesday, July 17th, 2012 in Web Development. Tags: , , , | 26 Comments »

+

26 Responses to ‘Shoot to kill; CSS selector intent’


  1. kierzniak said on 17 July, 2012 at 9:09 am

    Hi, very nice article. I have one question. What is the best te technique in your opinion to target nested sub elements e.g. in navigation?

    To prevent inheritance from parent elements I use combination with “>” selector. But at the end I have something like that:

    “.main-nav > li > ul > li > ul > li ”

    Which is not very elegant and efficient. I can use some classes to target each level of navigation but I will end with ugly html with tons of classes.


  2. Harry Roberts said on 17 July, 2012 at 9:12 am

    @kierzniak: I’d say a few classes in your HTML is a lot less ugly than sprawling selectors. With every new ‘level’ of navigation start again with a class, or abstract things out a little more and have something like: http://jsfiddle.net/csswizardry/Zj4eb/


  3. Michael Matyus said on 17 July, 2012 at 12:51 pm

    I’ve been employing the same practice as you but I’ve been calling it “Selector Namespacing”.

    http://jsfiddle.net/matyus/y5FYw/1/

    Labeling each element might be more extreme than some developers prefer but it’s been working out really well for me. (It also eliminates the need for visually indenting the children selectors.


  4. Art Lawry said on 17 July, 2012 at 2:52 pm

    This is even more important if you have a heavily modular site. Reminds me a little of Jonathan Snook’s SMACSS approach.

    Classes everywhere might not feel sexy, but I’d argue that it’s generally the lack of a system regarding class naming that’s ugly.

    People need to start thinking as classes as part of semantic markup. Good class names should explain what’s going on in the CSS without having to look at the CSS.

    I like thinking of classes like this as sub-classing in the object oriented sense:

    <ul class="generic more-specific very-specific">

    <ul class="nav horizontal-nav dropdown-nav">

  5. Joseph Silber said on 17 July, 2012 at 3:57 pm

    Correct me of I’m wrong, but I was always under the impression that pseudo selectors (as well as attribute selectors) are never the key selector.

    So, if that’s correct, the key selector in
    p:last-child
    would be the p, not the :last-child.

    Please do correct me if I’m wrong, since I’ve always operated under that assumption. Can’t remember where I got that from.


  6. Binyamin said on 18 July, 2012 at 2:35 pm

    You might fine a lots of crazy selectors even on the top worldwide websites.
    Like Google have mentioned, it is important to add meaning to your HTML semantics, IDs and class’es https://plus.google.com/110977198681221304891/posts/RYh56FMymW4


  7. Dave^ said on 18 July, 2012 at 2:38 pm

    I think this bit perfectly sums up the point of your article, and is a good mindset to have:

    “…ask yourself ‘am I selecting this because it’s a ul inside of .header or because it is my site’s main nav?’”

    Good post indeed!


  8. bryan said on 18 July, 2012 at 8:40 pm

    Going “crazy town” also kills page performance!

    If you can help it, it is often best to avoid referencing specific tag elements–especially common ones (like p, div, span, etc).

    The reason for this is the browser matches from right to left, and has to refine its list, which takes time.

    Unhappy example:
    #some.thing div.has .gone-wrong p {
    // do stuff
    }

    This will cause the browser to match EVERY paragraph first, then refine to those who are children of class .gone-wrong, etc.

    Best to assign a class (or id, as appropriate) to any elements and avoid the global matching.

    Happy example:
    p.bunny_love {
    // do stuff
    }


  9. Niels Matthijs said on 19 July, 2012 at 9:39 am

    I think the article mixes two things together.

    I completely agree that header > ul is a bad selector, only because it is too generic and far from future-proof. Then again, .main-nav isn’t great either. What happens when we want to repeat our main-nav in the footer? So header > nav.main .. that’s really what you’re intending to style.

    I wouldn’t really add anything about selector efficiency though. Just yesterday I checked on my own website (I actually like and endorse verbose css selectors, as long as they are “correct”) and for the most complex page css selector performance amounted to a measly 6ms, hardly worth worrying about. If the key of your selector is meant to be a tag selector, just let it be. It’s better than littering the html with extra styling hooks that are quite unnecessary.


  10. Gavin said on 19 July, 2012 at 9:46 am

    Does this mean that essentially all references to HTML elements in CSS should be reserved for base elements, typographical elements and some repeatable elements?

    Your article’s interesting because it leads towards how we give meaning to our markup. In a way you are defining your own tags, so is really .

    Do you think in the near future we will have things like or ?


  11. Phunky said on 19 July, 2012 at 10:06 am

    Unless you plan on having multiple elements styled with .main-nav then it should be an ID as its a unique element and is more specific.

    Now I know you don’t like using IDs but when you come to use any JavaScript ID based selectors are a huge performance boost of classes.

    As with everything it boils down to how your going to use it both now and in the future.


  12. Niels Matthijs said on 19 July, 2012 at 10:25 am

    IDs are for elements that can _only_ be unique on a page. If not, multiple instances (always a possibility) of a component will be hard to style. Future-proof coding!

    And again, if you time these “performance issues” you’ll be surprised about how marginal they really are.


  13. Harry Roberts said on 19 July, 2012 at 12:59 pm

    @Phunky: Never use IDs in CSS. In JS, yes, in CSS, no. It’s as simple as that, and a rule I encourage everyone to follow :)


  14. Phunky said on 19 July, 2012 at 3:38 pm

    @Harry Roberts: i’m not a fan of that, I understand you reasoning behind it but just don’t agree with it.

    Also i’m slightly worried that you say its fine for JS but not CSS, so would that mean you would have ul#main-nav.main-nav in your markup?

    Seems very redundant from a maintenance point of view.

    @Niels Matthijs: Future-proof coding?! Sounds more like optimising too early as for me it would be an ID until there is a requirement for multiples.


  15. Harry Roberts said on 19 July, 2012 at 3:51 pm

    If you’re using something as a JS hook you should likely be prefixing it with js- anyway, so you would need a separate class (or ID). I still stand by no IDs in CSS :)


  16. Troy Slaten said on 19 July, 2012 at 5:29 pm

    “css sniper”, I like that! going into resume, lol.


  17. Natalia Ventre said on 19 July, 2012 at 10:46 pm

    I basically agree with the article, but sometimes adding a class is not possible. I’ve done article > p:first-child{} or similar, when the content is dynamic or user generated, the pseudo classes are great to polish the design without adding complexity in the content publishing process.


  18. Panca Nugroho said on 20 July, 2012 at 12:12 am

    I wish I read this earlier *learning the hard way


  19. skip405 said on 20 July, 2012 at 9:32 am

    I’d say I agree only partially. Without a doubt .header ul is not the best option for the site’s main navigation, but… is it always so? Is a universal pattern the best one?

    Not taking into consideration the project itself it’s almost impossible to answer this question. Would your selectors differ when coding a large-scale project or a promo-site? – I’d say yes. Would I personally use .header ul on a promo-site? I’d say yes. Would your “future-proof” concerns differ from project to project? – I’d say yes. And a more important question is SHOULD they differ? – I’d say yes!

    Consider looking into an example:

    
    
      Pages:
    
        <a href="#" rel="nofollow">1</a>
        <a href="#" rel="nofollow">2</a>
        <a href="#" rel="nofollow">3</a>
    
    <!-- /.pagination_block -->

    I ask myself your question: ‘is it an ol I am styling or is it my pagination’? – Well, it’s my pagination, surely! But do I really need a class on the ol or .pagination_block ol will suffice? The latter imo. Which leads me to the conclusion that asking such a question is a good way, but it… well may not bring any good at all.

    I can compare this dilemma to some (probably not best) vivid life example. Consider answering the question “What’s the best way to kill a fly?” You may start thinking that there are small flies and big flies, there are few or there are many… and after all this thinking you come to the conclusion… a bazooka. It… well… will kill’em all, big or small, few or many, today or tomorrow, and it can be used against not only flies – which is also a kinda plus right? Yeah, why not fire a bazooka every time we see a fly? It’s universal for sure… but you will surely not do that, right? :) Same thing is here in my opinion. Universality is not always good!


  20. Marco Kotrotsos said on 20 July, 2012 at 11:44 am

    Actually- an attribute selector can also be key. ‘Key’ is just defined as ‘the rightmost selector’

    So

    .somefield [type=text] { }

    Means; let’s get all inputs with type text, and find me a .somefield.

    And p:last-child {} would mean Give me All children that are found to be last, and then find me a p. That is why that selector is very slow to match.


  21. Bart said on 20 July, 2012 at 3:15 pm

    I like this approach.

    Not sure if this is related, but what is the best practice for marking current page in your navigation? Do you add class=”current” to the li or the a? Or do you use /*body*/.pg-home /*a*/.link-home? I tend to use the last approach since I include the same nav code on all pages. For maintenance reasons it works best. If I have to add, edit, or remove a link, I just do it in the include file and it’s done for the site. Also, I use the same nav HTML for header and footer navigation. The current page indication is automatic. Would this depend on your personal workflow and tool set?

    As hypocritical as it may sound, I have to disagree with skip405 about using different approaches based on site size. Once you adopt the right strategy and form it into a habit, it’s just simpler to maintain one coding style than two. Just too much mental gymnastics if you ask me. After all, best practices should be portable and applicable in any situation.


  22. Peter F. said on 21 July, 2012 at 1:56 am

    I tought right most selector being a performance concern was over, why perpetuate old concerns that are now not a problem, the valid argument for not using a element type selector as the right most selector would be the reusable limitation and the style battle.


  23. skip405 said on 22 July, 2012 at 8:00 pm

    @Bart, yes, I do agree that my proposed solution is more difficult, but who said that it has to be easy? I thought the key phrase should be… ‘best’ or ‘right’. If it takes mental gymnastics, as you say, for a certain method to be the most appropriate for the current site – I think that’s just the way it should be and I’d go for it. And I shouldn’t care if it’s easy or difficult, I simply know that it’s the best solution, that’s it.


  24. Derek Kohn said on 23 July, 2012 at 10:58 pm

    This is a really valuable article and you made some great points. Naming conventions were always a vague and difficult thing for me early on. What happens is you typically will end up backing yourself into a corner with crazy class names.

    My solution is try not to over think it. Name it for what it is and don’t be ambiguous unless it is an id.

    Nice work! Shared


  25. Peter W. said on 26 July, 2012 at 12:52 pm

    This is an interesting article, but it’s mostly just opinion. Coming out with “bold” statements about “right” and “wrong” ways to do CSS seems to have become increasingly fashionable recently. Honestly, and with all due respect, I have little use for such things. If it helps you or anyone else with their work, then more power to you. I don’t think it helps people who are just starting out to tell them “don’t ever do this or that”. Tell them to find their own way, learn from their mistakes, and so on. That’s how you learn what works best and what doesn’t. I use IDs, and for good reasons. No one has to use them — that’s the beauty of this. Personally I don’t like to see CSS that is sloppy or done carelessly. Things like using four or five rules where you could have used one or two. I also don’t like seeing html littered with dozens of unnecessary classes. You need to find a balance that works for you and the site in question.There are all kinds of ways to make CSS (and I’m talking about more than just selectors) more efficient and maintainable (as opposed to the mythical “future-proofing”). But these ought to be based on the specifics of each site. Period.


  26. Bart said on 26 July, 2012 at 8:08 pm

    @skip405 I think we’re both after the same thing…the best or most correct way of coding our CSS. Where I disagree is that the approach to coding CSS should vary based on scale of the site. I think there is one best way to code a site regardless of context. Because it’s a small site doesn’t mean one should use bad habits and bad practices just because it’s “easier” to do. Once one develops the good habits it will become easier for them to apply them everywhere. While learning the good habits, it might be less efficient to turn out a small site, but that does not justify using poor coding techniques. I used to write table-based layouts quite comfortably, but gave that up for CSS-based layouts. I wasn’t too efficient at CSS layouts at first, but if you asked me now to write a table-based layout I wouldn’t even know where to begin.


Leave a Reply

Respond to Shoot to kill; CSS selector intent

Hi there, I am Harry Roberts. I am a 21 year old web developer from the UK. I Tweet and write about web standards, typography, best practices and everything in between. You should browse and search my archives and follow me on Twitter, 7,791 people do.

via Ad Packs