Have questions about buying, selling or renting during COVID-19? Learn more

Zillow Tech Hub

Understanding CSS Specificity

For two years, I have been conducting front-end engineering interviews at Zillow. While every interview is a little different, one thing stands out as common to all of them: hardly anyone understands how CSS specificity works (TL;DR including me).

The question I ask is this:  what is the color of the text in the following snippet of code?


#a .b .c { color: red; }
.d .e .f { color: green; }
.g .h #i { color: blue; }


<div id="a" class="d g">
    <div class="b e h">
        <div id="i" class="c f">What is the color of this text?</div>
    </div>
</div>

If you said blue, you are correct. But what happens when I swap the first and last rule? What happens when I add an element to the first selector? Or what if the second rule had 1000 classes instead of 3? Usually at this point, the candidate starts to question everything they know about CSS.

Understanding how to calculate the specificity of a rule is an extremely useful skill, but the purpose of this post is not to teach you how to do that (there are several good resources that already do this better). I am interested in exploring my last question above:

What if the second rule had 1000 classes instead of 3?

When I ask this question, candidates will sometimes change their answer to green. I think this comes from an idea that selectors each have a number value that can be added together for a total weight; if this were true then 1000 classes could very easily trump a single ID. I don’t necessarily think this is a bad way to conceptualize specificity, but it is not technically correct. Consider the following example where IDs are worth 100 and classes are worth 10:


#a { color: red; } /* 1 * 100 = 100 */
.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z { color: blue; } /* 25 * 10 = 250 */


<p id="a" class="b c d e f g h i j k l m n o p q r s t u v w x y z">What is the color of this text?</p>

By point value, the color of the text should be blue, but here you can see the color is actually red. This example is contrived, but it clearly shows that the point values have no effect on specificity. Or do they?

Before creating the above example, the answer “green” came up frequently enough that I began to question myself. I searched the web and found examples both for and against assigning point values to selectors. I decided to go straight to the CSS3 spec to figure out my answer. Surprisingly, this is the example they give:


*               /* a=0 b=0 c=0 -> specificity = 0   */
LI              /* a=0 b=0 c=1 -> specificity = 1   */
UL LI           /* a=0 b=0 c=2 -> specificity = 2   */
UL OL+LI        /* a=0 b=0 c=3 -> specificity = 3   */
H1 + *[REL=up]  /* a=0 b=1 c=1 -> specificity = 11  */
UL OL LI.red    /* a=0 b=1 c=3 -> specificity = 13  */
LI.red.level    /* a=0 b=2 c=1 -> specificity = 21  */
#x34y           /* a=1 b=0 c=0 -> specificity = 100 */
#s12:not(FOO)   /* a=1 b=0 c=1 -> specificity = 101 */

Point values don’t work like that (as discussed above), but the spec also says this:

Concatenating the three numbers a-b-c (in a number system with a large base) gives the specificity.

Maybe base 10 was not large enough to disprove the point value system. If the number system was base 100, then classes would be worth 100 and IDs 10000. This would change the values of my previous example in such a way that red would be the correct color:


#a { color: red; } /* 1 * 10000 = 10000 */
.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z { color: blue; } /* 25 * 100 = 2500 */

This would also mean that in my original question, 1000 hypothetical classes would take precedence and make the text green! The next logical step is to test my question specifically. At first I thought this would be a tedious exercise, adding 1000 unique classes to the example, but in the process of digging through the spec, I learned that “repeated occurrences of the same simple selector are allowed and do increase specificity.” So without further ado, here is my original question with 1000 classes in the second rule.

Surprised? If not, go back and check in Firefox or Internet Explorer, I’ll wait…

Turns out, in Chrome and Safari, the text is blue as I suspected, but in Firefox and Internet Explorer, the text is actually green! So if earlier examples worked as expected, and this one didn’t, that means Firefox and Internet Explorer must be using a base somewhere in between 10 and 1000. After playing with the example a little more, I found that it took exactly 259 classes in the second rule before the color changed from blue to green: 2 classes to match the two classes in the third rule, 1 class to tiebreak, and 256 classes to match the ID in the third rule — base 256.


#a .b .c { color: red; }    /* 1 * 256^2 +   2 * 256 = 66048 */
.f x 259 { color: green; }  /* 0 * 256^2 + 259 * 256 = 66304 */
.g .h #i { color: blue; }   /* 1 * 256^2 +   2 * 256 = 66048 */

I tried to find a base that would work for Chrome and Safari, but the browser would crash before I could add enough classes to make a difference. Either the base is extremely high, or the implementation is such that the values do not overflow (while not mentioned in the CSS3 spec, the Selectors Level 4 editor’s draft specifies that there should be no overflow).

When I started to write this post, I was ready to debunk a common misconception about CSS specificity (and I was convinced I had done so until I tested my examples in IE). Instead, I stumbled upon an ambiguity in the CSS3 spec, and an edge case with different browser implementations. Overall, I discovered that just like everyone else, I did not understand how CSS specificity truly worked!

Understanding CSS Specificity