What the Heck Is CSS Specificity?

by on 21st June 2013 with 18 Comments

screenshot

CSS specificity is a topic that many new front end coders avoid for as long as possible. It sounds complicated, there are all of these rules, you might even have to do some math! How lame is that?

Ultimately, you can only avoid it for so long. Specificity is an essential concept that you need to grasp to be an effective developer. Today I’ll walk you through the concepts of specificity in a simple and easy to understand manner. It’s easier than you think!

The Core Concept of Specificity

Specificity is a funny word, right from the outset it communicates the idea that this is going to be complicated. Fortunately, its bark is bigger than its bite. There’s no reason that you can’t pick most of this stuff up in a few minutes or less.

To break the practical application of specificity down into something that anyone can wrap their mind around, let’s use a familiar metaphor.

Who Would Win in a Fight?

When you were a kid, the world was a much cooler place. If given the opportunity to ask one and only one question to an all knowing entity, you would not have pondered the meaning of life or ventured forth a query about whether or not world peace is attainable, only one thing would matter. If Batman and Spiderman got into a fight, who would win?

screenshot

ScrewAttack writes huge pieces dedicated to pitting heroes against each other.

There’s this innate curiosity in children to constantly compare things and ask which is better. As we grow older, shades of gray replace black and white and we lose this tendency (we nerds are of course an exception). To understand CSS specificity, you should revert to childhood. When given two similar selectors, always ask yourself, “Who would win in a fight?” The answer could prove critical to the way you code.

To see how this works, let’s pit a few selectors against each other and see what happens.

And the Last Shall Be First

For our first battle, let’s pit two essentially identical selectors against each other. How do we know which selector would win?

screenshot

Talk about class warfare. Puns aside, imagine that we had the following setup on our web page:

<!-- HTML -->
<div class="blue box"></div>
/*CSS*/
div {
  height: 200px;
  width: 200px;
}

/*The Battle*/
.blue {
  background-color: blue;
}

.box {
  background-color: red;
}

Here we have an equal matchup. Both classes are applied to the same div with no other complexities to muck up the example. Let’s let them battle it out and see who wins…

screenshot

The box is red! That means the “.box” class won. But why? You probably already know the answer right? It came last. Switch the selectors around and we see the opposite outcome:

screenshot

Here the square is blue because the “.blue” class came last, overriding the “.box” class.

Class Doesn’t Change Who You Are

That last battle wasn’t very interesting. The opponents were too equally matched and the outcome was obvious. We haven’t even really scraped the surface of specificity yet. Nevertheless, that was a necessary foundation that needed to be laid.

Now we’ll dive into a battle that’s a little more juicy: ID vs. Class.

screenshot

Our setup here is pretty similar to last time, only instead of two classes, we’ll have one class and one ID:

<!-- HTML -->
<div class="box" id="box"></div>
/*CSS*/
div {
  height: 200px;
  width: 200px;
}

/*The Battle*/
#box {
  background-color: blue;
}

.box {
  background-color: red;
}

Given the rule that we learned last time, you might predict that the class would win in this scenario. Let’s see if that proves true.

screenshot

An upset! Even though the class came after the ID, it still lost. This indicates that IDs are somehow stronger than classes in the battle of specificity.

Pseudo Death Match

Before we jump in to see how all of this works, let’s look at one final example. We’ll ramp up the complexity here so you can begin to appreciate how this knowledge can be applied in a real world situation.

screenshot

Oh snap! I’ll bet this one even trips up some of the experienced readers. Many of us don’t even know the difference between the two, much less which one wins a specificity death match. Let’s get this party started.

<div>
   <h2>h20</h2>
</div>
h2:first-child {
  color: blue;
}

h2::first-line {
  color: red;
}

What’s it going to be? Will the h2 be red or blue? Let’s find out:

screenshot

The pseudo element takes it! The pseudo class didn’t stand a chance. But why? What behind the scenes voodoo is taking place here? How can we be sure of the outcome before we even try it?

Rules of Specificity

Trial and error is a great way to learn, but ultimately there are just too many different possible scenarios to run through to glean all of the information that we should know. What we need is a hard and fast way to decide which selectors the browser places more importance on and why.

It turns out, there are in fact simple rules that govern specificity. In fact, there’s even a handy point system:

screenshot

A given CSS selector can have any number of the pieces of this puzzle. The trick to figuring out specificity is to add them up. The one with the highest score wins. It’s that simple.

Nobody likes math though, so to test the specificity of a selector, we can use the awesome specificity calculator on Keegan Street.

screenshot

With this great tool, we can type in two selectors and a score for each will automatically be calculated. The first selector in the example contains one class, one pseudo-class and two elements (score: 0,0,2,2). The second selector beats it hands down with one ID, one class, one pseudo class and one element (score: 0,1,2,1).

The rule here, as you can see, is that the selector with the highest degree of specificity wins. In other words, more specific selectors trump less specific selectors.

Given our previous example of a class vs. an ID, we can see why the ID wins. Its specificity is far higher than that of the class, so it overrides the class.

Special Rules

Given the specificity calculator, we can figure out most scenarios, but there are a few curve balls that we need to keep in mind.

  • The universal selector (*) is worth 0
  • When two selectors have the same specificity, the last one wins
  • Elements can never beat a class selector, even if you pile them on
  • !important is Superman, it can beat up almost anything

The last one here is the most interesting one, so let’s take a look at an example. Here’s some HTML and CSS to consider:

<!-- We'll use this to test the effects of important -->

<h2 class="header">Some Headline</h2>
h2 {
  color: blue;  
}

.header {
  color: red;  
}

Simple enough, right? Using our point system, the class scores higher than the element, so the headline will come out red. But what if we toss Superman into the scenario?

h2 {
  color: blue !important;  
}

.header {
  color: red;  
}

Now we’ve rigged the fight. In this case, the headline actually comes out blue! The !important rule says, “screw you specificity” and does what it wants.

Less is More

Now that you understand specificity, you’ll be able to manipulate your CSS to do a lot of crazy things. Just remember that with great power comes great responsibility.

In CSS, the general rule is that less is more. The shorter your rules, the better. Never use .list ul > li > a when li a will work just fine. Shorter selectors are more efficient and easier to read. Sometimes you have to get fancy, but let that be the exception, not the rule.

A Puzzle to Solve

Wait a dang second. The rules were supposed to explain the results that we received in our initial tests, weren’t they? So what about this one?

screenshot

As far as I understand the rules, a pseudo class beats a pseudo element, so why does the pseudo element win here? Maybe we’re wrong about which is the pseudo element, let’s check our chart.

screenshot

Nope. We were right about first-child being the pseudo class versus first-line, the pseudo element. Maybe we’re calculating it wrong? Let’s consult the calculator:

screenshot

As far as the calculator is concerned, the headline should be blue because first-child beats first-line. But that’s not how it works in the browser. What gives? Maybe our calculator is broken, let’s try another one.

screenshot

According to this one too, first-child should have a higher specificity value and should therefore win… but it doesn’t. I’ve tried using the single colon syntax, switching the order; nothing works. The pseudo element always wins, no matter what I throw at it.

body {color: blue;}
h2 {color: blue;}
div h2:first-child {color: blue;}
h2:first-child {color: blue;}
.someClass {color: blue}

/*Wins Every Time*/
h2::first-line {color: red;}

This is where it really gets nuts, even if I toss in an inline style element, which should beat almost anything, the dang thing still comes up red!

<!-- Even this doesn't work! -->
<h2 style="color: blue;">h20</h2>

Conceptually, this almost makes sense. Our pseudo class is targeting the entire h2 element while our pseudo element is targeting only the first line of that element, which seems more specific.

Your Explanation Here

To be blatantly honest, I’m a little baffled here. For the most part, specificity is pretty straightforward, but I can’t seem to wrap my mind around this one. My instinct is that it’s perhaps some sort of inheritance issue, but I’m not sure. It might also be something similar to :not() which has special significance in regard to specificity (only the items in the parentheses count, by itself it’s a zero).

In response to our little puzzle, here are some helpful answers from readers!

Tim Pietrusky
“According to the spec only another element wrapping the content of the first line is more specific than the first-line pseudo-element.” – CodePen Demo

Thomas
“Think of first-line as an additional Element (like span) inside of the headline, wrapping only the first line, so its target is an element inside of the h2, like <h2><span>first line</span> second line</h2> The background-color of the h2 would only be used (inheritted) if no value would be declared for ::first-line”

Further Reading

There have already been a lot of great articles published on specificity, here are a few:

Conclusion

As we just saw, CSS specificity is super simple, except when it’s super complex… You can and should learn the basics so that you can avoid any unexpected results, but when it comes down to it, some good old fashion trial and error might be necessary if you come across a sticky situation.

What do you think? Do you understand specificity as much as you should? Can you explain the puzzling scenario that we ran into above?

Comments & Discussion

18 Comments

Comments & Discussion

18 Comments

  1. Thomas says:

    Think of first-line as an additional Element (like span) inside of the headline, wrapping only the first line, so its target is an element inside of the h2, like <h2><span>first line</span> second line<h2>

    The background-color of the h2 would only be used (inheritted) if no value would be declared for ::first-line

    • This is exactly right – I was getting ready to post the same thing. The pseudo-element example used isn’t about specificity, it’s about inheritance.

      To exemplify Thomas’ response, see this link: http://jsfiddle.net/vyKkS/4/

      Because of how inheritance works, the style applied directly to a specific element (even a pseudo-element) will always have priority over an inherited value – even if the specificity of the inherited value’s selector is greater.

  2. Mike says:

    “The spec is RRRRRRRRRRRRRRRRRROOOYAL BLUE!” -Jim Carrey

  3. Sitebee says:

    Could you point me in the right direction where I can find more tutorials in CSS3 design and layouts.

  4. According to the spec http://www.w3.org/TR/CSS2/selector.html#first-line-pseudo only another element wrapping the content of the first line is more specific than the first-line pseudo-element.

    http://jsfiddle.net/TimPietrusky/2tw5w

    • Joshua Johnson says:

      Thanks Tim! I included this in the article.

    • This is where things get messy and we have to bring up the idea of the shadow DOM.

      Note that, even if you move the p:first-line selector to after the p span selector, the styles will not change; even though they both have a value of 0,0,0,2.

      This happens because pseudo-elements are actually a part of a shadow DOM – nodes that exist only to the browser (but are still available for styling). Our resulting browser-seen DOM for this element is:

      Lorem ipsum dolor sit amet, …

      So, again, because the span styles will be used because the styles that would be inherited from the ::first-line shadow DOM node are being overwritten.

      • Let’s try that markup example again, shall we:

        <p>
          <shadowDOM::first-child>
            <span>Lorem ipsum dolor site amet,</span> …
          </shadowDOM::first-child>
        </p>

        Hopefully that works and doesn’t actually show the html entities…

  5. Eddie Foreman says:

    Reminds me of the old blog on CSS specificity wars – a must for all star wars fans ( http://bit.ly/2asuN3)

  6. Rob says:

    How does specificity play with performance/speed? Does it? Is there any difference or it just has to do with priority? Yes… I’m a noob.

  7. Thierry says:

    h2:first-child {} targets a *node* inside h2 (first or single one), since the h2 only contains a string it is normal that the text is not styled by that rule.

    • Not exactly – since :first-child is a pseudo-class and not a pseudo-element, it targets an h2 that is also a :first-child, just like h2.first targets an h2 with the class of “first” (and not an element with the class of “first” inside the h2).

      Fiddle: http://jsfiddle.net/WvaLW/

      • Thierry says:

        Duh! Of course. What I was thinking about!?
        I guess I looked at it as if it was written as h2 :first-child {} (h2:first-child)

        Thanks for correcting me!

      • Thierry says:

        Today is not my day I guess. I should have used “[space]” to mark the space between the “h2″ and the pseudo-class. Let’s try one more time: (h2< space >:first-child)

  8. Matt says:

    And for the truly insane, Chris Coyier pointed out that 256 classes will override an ID: http://codepen.io/chriscoyier/pen/lzjqh

  9. Some really useful advice here. Thanks!

Leave a Comment

Subscribe
Membership
About the Author