The following is an extract from our book, CSS Master, written by Tiffany B. Brown. Copies are sold in stores worldwide, or you can buy it in ebook form here.
Think of specificity as a score or rank that determines which style declarations are ultimately applied to an element. The universal selector (*
) has low specificity. ID selectors are highly specific. Descendant selectors such as p img
and child selectors such as .panel > h2
are more specific than type selectors such as p
, img
, or h1
.
Calculating exact specificity values seems tricky at first. As explained in Selectors Level 3, you need to:
count the number of ID selectors in the selector (= A)
count the number of class selectors, attribute selectors, and pseudo-classes in the selector (= B)
count the number of type selectors and pseudo-elements in the selector (= C)
ignore the universal selector
These A, B, and C values are then combined to form a final specificity value. An ID selector such as #foo
has a specificity of 1,0,0. Attribute selectors, such as [type=email]
and class selectors such as .chart
have a specificity of 0,1,0. Adding a pseudo-class such as :first-child
(for example, .chart:first-child
) gives us a specificity of 0,2,0. But using a simple type or element selector such as h1
or p
only gives us a specificity of 0,0,1.
Note: Calculating Specificity
Keegan Street’s Specificity Calculator and Joshua Peek’s CSS Explain are helpful for learning about and calculating selector specificity.
Complex and combinator selectors, of course, give us higher specificity values. Let’s look at an example. Consider the following CSS:
ul#story-list > .book-review {
color: #0c0;
}
#story-list > .book-review {
color: #f60;
}
These two rule sets are similar, but they are not the same. The first selector, ul#story-list > .bookreview
, contains a type selector (ul
), an ID selector, (#story-list
), and a class selector (.bookreview
). It has a specificity value of 1,1,1. The second selector, #story-list > .book-review
only contains an ID and a class selector. Its specificity value is 1,1,0. Even though our #story-list > .book-review
rule succeeds ul#story-list > .bookreview
, the higher specificity of the former means that those elements with a .book-review
class will be green rather than orange.
Pseudo-classes such as :link
or :invalid
have the same level of specificity as class selectors. Both a:link
and a.external
have a specificity value of 0,1,1. Similarly, pseudo-elements such as ::before
and ::after
are as specific as type or element selectors. In cases where two selectors are equally specific, the cascade kicks in. Here’s an example:
a:link {
color: #369;
}
a.external {
color: #f60;
}
If we applied this CSS, every link would be slate blue except for those with class="external"
applied. Those links would be orange instead.
Keeping specificity low helps prevent selector creep, or the tendency for selector specificity and length to increase over time. This often happens as you add new developers to a team, or new forms of content to a website. Selector creep also contributes to long-term maintenance headaches. You either end up using more specific selectors to override other rule sets, or needing to refactor your code. Longer selectors also increase the weight of your CSS files.
We discuss strategies for keeping specificity low in Chapter 2.
Conclusion
After reading this chapter, you should have a good understanding of CSS selectors. Specifically, you should now know how to:
-
use selectors to apply CSS to particular elements, pseudo-elements, and pseudo-classes
-
understand the difference between pseudo-elements and pseudo-classes
-
employ newer pseudo-classes introduced by the Selectors Level 3 and 4 specifications
-
calculate specificity
In the next chapter, we’ll address some golden rules for writing maintainable, scalable CSS.