Getting unspecific with CSS
Anyone familiar with CSS will have encountered the concept of specificity, a nifty mechanism that allows web browsers to resolve conflicts between CSS declarations. Specificity is an essential component of the CSS cascade, and most of time it works in predictable ways which make your job simpler. However if you get too specific with your CSS selectors you might be making your stylesheets more complex than they need to be.
Specificity in action
Before we tackle the topic of over-specificity, let’s brush up on the basics. Here is a simple CSS/HTML snippet in which we want all h1
headings to be blue, except for those which appear in a section
element:
h1 { color: blue; }
section h1 { color: red; }
<h1>I am blue!</h1>
<section>
<h1>Big deal, I am red!</h1>
</section>
When a browser renders the h1
that is nested within the section
element, it resolves the conflict between the two CSS rules by giving priority to the more specific selector: section h1
.
In this example it’s easy to guess that the nested h1
is going to be red, but specificity isn’t always so easy to grasp. Things get trickier when you start throwing IDs and classes into the mix, and in these cases it helps to understand how specificity is calculated.
Calculating specificity
When resolving a conflict between two rules, the cascade assigns the greatest weight to the rule with the most ID selectors. If the results are tied then the rule with the most class selectors wins out, and failing that the rule with the highest number of element selectors trumps any competing rules.
Many online resources state that specificity is calculated by assigning a numeric value to each selector, then adding them together: ID selectors are assigned a value of 100, class selectors a value of 10, and HTML elements a value of 1. This was how CSS1 worked, but CSS2 introduced the new calculation method which I outlined above. For most intents the two methods are identical, but results do differ when dealing with large numbers of selectors.
If you want the nitty gritty, the Sitepoint CSS reference explains Specificity calculation in great detail. If you find Sitepoint’s examples too difficult to digest then Andy Clarke’s humorous Specificity Wars might be more your cup of tea.
Here is another example, showing the impact that an ID selector has on specificity. I’ve added a couple more HTML elments into the mix (you’ll see why soon), but we’re still trying to make the first h1
blue, and the nested h1
red:
#content h1 { color: blue; }
section h1 { color: red; }
<div id="content" class="home">
<article class="blog-post">
<h1>I am blue!</h1>
<section>
<h1>Big deal, I am red! Or am I?</h1>
</section>
</article>
</div>
At first glance the second selector may appear to be more specific because it contains a reference to section
, an element that is closer in proximity to the target heading. In fact the first rule will win out, and both h1
‘s will be blue. This is because the first rule includes an ID selector, while the second rule contains only element selectors. Remember that an ID selector trumps any number of class or element selectors.
To get the second color declaration working we would need to add a #content
ID selector to it. This makes the selector more specific, and the nested h1
will now be red as intended:
#content section h1 { color: red; }
Don’t over-specify
Complexity begets complexity, and the more specific a CSS selector is, the more difficult it becomes to overrule it. Imagine if our ‘blue’ selector had looked like this:
div#content.home article.blog-post h1 { color: blue; }
Talk about overkill. As well as an ID selector, the rule now also contains two class selectors and two additional element selectors. This forces us to make our ‘red’ rule even more specific to ensure its color
declaration is respected:
div#content.home article.blog-post section h1 { color: red; }
This is clearly a much less efficient solution than the earlier example. Our ‘red’ rule now requires one ID selector, two class selectors, and four element selectors to achieve what before we were doing with just one ID and two element selectors.
As well as being difficult to read, verbose CSS selectors make it harder to target nested elements. Conflicts may be easy to diagnose using a DOM inspector such as Firebug, but isn’t it better to avoid having to hunt for problems in the first place?
The solution is to make your selectors only as specific as they need to be. Use as few selectors as possible to achieve the desired result, and avoid adding extra ID or class selectors into your rule unless they really needs to be there. By crafting your selectors to be as unspecific as possible you can keep your stylesheets simple and readable, and preserve your sanity.