In our attempts to understand CSS, we tend to focus closely on properties and values, and how they interact with each other. In the rush to learn all the Cool Stuff, we often forget that the basics are not only necessary, they're often more important than any single property, no matter how cool it might be.

A perfect example of this is the concept of specificity, which is in many ways the underpinning of everything that is CSS. It seems simple-- a basic numeric mechanism for determining which selectors are more specific than others-- but upon a little digging, it soon becomes apparent that specificity is not only fraught with unexpected complications, it's also one of the least-understood aspects of CSS. It's time that was corrected.

What Specificity Does

At its core, specificity is the mechanism by which the rest of CSS works. It is used to decide which selectors are more specific than others, and therefore which rules will apply to a given element. For example, consider the following:

p.warning {color: red;}
div table p {color: magenta;}

Okay, now assume that you have a paragraph with a class of warning which is found inside a table which is contained within a div. Both rules apply to the paragraph in question-- so which one is used? Our first reaction might be to say that the second wins, because it comes later in the stylesheet. That is, after all, one of the rules of CSS. However, the rules state that if two selectors have the same specificity, origin, and so on, then the later one wins.

Our example rules don't have the same specificity. Therefore, the rule whose selector has a higher specificity will win, and warning paragraphs will be the appropriate color. In this case, it's the first rule, so they'll be red. (We'll see why in the next section.) However, if we change the rules like this:

p.warning {color: red;}
div table p.warning {color: magenta;}

Now the second rule has a higher specificity, so it wins out, regardless of whether it comes before or after the first rule. In other words, I could also have reversed the order of the rules, and the 'magenta' rule would still be more specific than the 'red' one. Thus warning paragraphs inside tables which are inside divs will be magenta. Of course, warning paragraphs in any other context will still be red, since the 'red' rule matches all warning paragraphs, regardless of their location.

Calculating Specificity

The rules for determining a selector's specificity are at once incredibly simple and surprisingly complex. We'll start with the simple and work our way up from there.

The rules, as they are laid out in the CSS specification, states that the pieces of a selector are assigned specificity as follows:

Now here's where the rules are dangerously misleading, so don't jump to any conclusions right away. According to the CSS specification, the three numbers are concatenated together in the order a-b-c, so a selector with a class selector and two element selectors would yield a specificity of 12. Wait! If you're thinking that this will give us regular numbers in base ten, undo that thought!

In fact, the specification says that the three numbers are concatenated "in a number system with a large base." What they meant by that was something higher than base ten, base sixteen, or really any finite base. So we're not going to do it the spec way. Instead, we're going to represent specificity as a comma-separated list of numbers.

Let's bring back our original warning paragraph rules to see why this is important. The rules were:

p.warning {color: red;}
div table p {color: magenta;}

Okay, the first rule has an element selector (p) and a class selector (.warning). Therefore, its specificity is 1,1. The second rule has three element selectors, so its specificity is 3. The first rule wins because it has numbers in the "b" and "c" columns, while the second rule only has numbers in the "c" column. We'll also consider the specificity of our second warning rules:

p.warning {color: red;}
div table p.warning {color: magenta;}

Here, the first rule's specificity is 1,1 and the second's is 1,3. That's why the second one wins in this situation.

Comma Chameleon

Why all the hullabaloo about commas and base ten? Because of the way the specification gives its examples, and how that's misled many an author. The specification would have represented the specificities previous example as 11 and 13. That might seem harmless enough-- after all, the correct rule won-- but it implies that in the following code block, the second rule should win, when it fact it won't:

p.aside {color: red;}
html body div table ul li ol li ul li ul li p {color: cyan;}

Assume that we have a paragraph which matches the second rule, and which also has a class of aside. Thus both rules will match this paragraph. The specification would represent the specificities of these rules as 11 and 13, respectively. This implies that our example paragraph will be cyan. It will, in fact, be red. To see why, let's go back to the comma notation: 1,1 and 13. The true nature of the specificity is much more obvious using the comma notation.

Actually, the best possible notation would be to always represent a, b, and c in every specificity value. Thus:

h1 em {color: red;}               /* 0,0,2 */
em.howdy {color: yellow;}         /* 0,1,1 */
ol#checklist li blockquote {color: green;}
                                  /* 1,0,3 */
:hover:link {color: blue;}             /* 0,2,0 */
html body div div ul li ul li ol li code {color: purple;}
                                  /* 0,0,11 */

Just by looking at these numbers, we can see that the correct specificity sort for these rules is 3-4-2-5-1, going from most to least specific.

Pseudo-specificity, Inheritance, and More

There are a few interesting things which need to be covered before we finish this discussion. The first is that pseudo-elements (:first-line and :first-letter, for example) have no specificity at all. The following rules will therefore have the indicated specificities:

p:first-letter {color: red;}  /* spec. = 0,0,1 */
p {color: blue;}              /* spec. = 0,0,1 */

The only reason the first letters of paragraphs will be red is because the pseudo-element which is created to apply styles to the first letter allows the explicitly declared styles to override any inherited styles-- thus, red beats out blue in this circumstance.

Pseudo-classes, on the other hand, have the same specificity as regular classes. Thus:

a:link {color: blue;}        /* spec. = 0,1,1 */
a:visited {color: purple;}   /* spec. = 0,1,1 */

Again, we have two rules with the same specificity. In this case, the two rules apply to mutually exclusive states of hyperlinks. Since a:link applies to any unvisited hyperlink, and a:visited to any visited link, these rules can never apply to the same link at the same time. However, any link can be active or hovered, regardless of its visited state. This is why hover and active rules have to come after the link and visited rules. Assume that we put the hover first, for example:

a:hover {color: red;}        /* spec. = 0,1,1 */
a:link {color: blue;}        /* spec. = 0,1,1 */
a:visited {color: purple;}   /* spec. = 0,1,1 */

Now assume a link which points to a page you've visited. The hover and visited rules both apply, so the later one wins-- in this case, the visited rule. The same logic holds for any unvisited link. Therefore, you'll never see the hover effect unless you put the hover rule after the other two:

a:link {color: blue;}        /* spec. = 0,1,1 */
a:visited {color: purple;}   /* spec. = 0,1,1 */
a:hover {color: red;}        /* spec. = 0,1,1 */

Similar logic holds for :active, which must come after any other rule you don't want to override your active effect. This is also why CSS2 permits stringing multiple pseudo-classes together, like this:

a:link:hover {color: magenta;}

For more information on this technique, see the article A Pseudo Makeover.

There is one area where specificity as given in the CSS specification and the way it's been implemented clash. This has to do with inline styles; that is, those styles given as values of the style attribute. The specification says that any inline style is treated as though it is given as an ID selector at the end of the document's embedded stylesheet. Consider:

<style type="text/css">
p#topquote {color: green;}  /* spec. = 1,0,1 */
</style>
<p ID="topquote" style="color: red;">...</p>

According to CSS, the color of the paragraph in this example should be green. Why? Because the inline style is given a specificity of 1,0,0, but the rule found in the embedded stylesheet has a specificity of 1,0,1-- so it wins out.

According to browsers, though, an inline style has a specificity more like 1,0,0,0, thus allowing it to win over any ordinary rule found in any other source. This behavior is based on the argument that authors expect their inline styles to win out over everything else (with the exception of !important rules, which always beat everything). So creating a test page with the example styles and markup would almost certainly result in a red paragraph, not a green one. A resolution to this clash between specification and implementation has yet to be found.

It's important to remember one more thing: that inherited values always have a specificity of 0,0,0. Take this, for example:

.help div#navbar table#side {color: white;}  /* spec. = 2,1,2 */
p {color: blue;}                             /* spec. = 0,0,1 */

Thus, any paragraph inside a table with an ID of side that's contained by a div with an ID of navbar, all of which is contained within an element with a class of help, will have its text be colored... blue. The explicitly assigned value overrides any inherited value, no matter how specific a selector might be.

Conclusion

Simple or not, specificity is a cornerstone of the CSS specification, and it's all too often misunderstood. By thoroughly understanding its behavior, authors can avoid a number of common mistakes in selector construction and the order in which selectors are written. The payoffs range from diagnosing places where one selector vastly outweighs another to figuring out which pseudo-classes need to come last. Although the clash over inline style specificity has yet to play out to its conclusion, for the moment authors can generally rely on inline styles winning out over embedded or external styles. All in all, it behooves every CSS author to study specificity closely and understand it completely. I hope this article has gotten you started on that journey.