What the Heck Is CSS Specificity?

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?

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?

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

<!-- HTML --> <div class="blue box"></div> 1 2 <!-- HTML --> <div class = "blue box" > </div>

/*CSS*/ div { height: 200px; width: 200px; } /*The Battle*/ .blue { background-color: blue; } .box { background-color: red; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 /*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…

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:

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.

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> 1 2 <!-- HTML --> <div class = "box" id = "box" > </div>

/*CSS*/ div { height: 200px; width: 200px; } /*The Battle*/ #box { background-color: blue; } .box { background-color: red; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 /*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.

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.

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> 1 2 3 <div> <h2> h20 </h2> </div>

h2:first-child { color: blue; } h2::first-line { color: red; } 1 2 3 4 5 6 7 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:

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:

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.

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> 1 2 3 <!-- We'll use this to test the effects of important --> <h2 class = "header" > Some Headline </h2>

h2 { color: blue; } .header { color: red; } 1 2 3 4 5 6 7 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; } 1 2 3 4 5 6 7 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?

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.

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:

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.

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;} 1 2 3 4 5 6 7 8 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> 1 2 <!-- 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

first line second line

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?