CSS Specificity Scoring

Most folks realize what the "cascading" part of Cascading Style Sheets means for styling Web pages. However, some of the inner workings of CSS specificity are lost on many of them.

Specificity In General

First, a refresher in CSS specificity:

Tobby Hagler, Director of Engineering
#Drupal | Posted

Most folks realize what the "cascading" part of Cascading Style Sheets means for styling Web pages. However, some of the inner workings of CSS specificity are lost on many of them.

Specificity In General

First, a refresher in CSS specificity:

There are five sources of style definitions: Browser defaults, HTML Elements, Classes, IDs, and Inline Styles (six if you include user overrides). Each source takes a higher precedence over the previous. For instance, a header tag will have font-size and margins applied by the browser, but these are easily reset by simply declaring something like h2 { font-size: 24px; margin: 10px 0 0; } (an element selector) in your style sheet. This can be taken further with declaring h2.smaller { font-size:16px; }, which will shrink headers with a class of "smaller" (the margins will stay the same, however).

Like with most things, order matters. Especially when dealing with multiple external style sheets (common with just about any content management system, and especially with Drupal), the order that a CSS selector is declared makes a big difference. The latter that definition appears in the chain of selectors, the more likely it is to be the style definition that's actually rendered in the browser.

Selectors can be compounded in order to specifically target an element on a page. For instance, #left-column h2 { color: #900; } will cause certain headers in the left column of your page to be red, but they will otherwise keep any styles declared in h2 { ... }. All other header2 tags will be unaffected. This parent/child relationship is common.

However, the CSS large sites can get confusing fast. Selectors are redeclared for different page types, multiple modules might try to override each other, etc. It's important to understand how to quickly determine which selector will be used beyond simply re-ordering style sheets or nesting your elements.

How Specificity Scoring Works

Think back to grade school, when your teacher was explaining numbers' "places". You have ones, tens, hundreds, etc. CSS specificity works much the same way. Each type of CSS element has a different "place". The only real difference is that when a place reaches 10, it doesn't carry over to the next place.

  • HTML Element - Ones
  • Class - Tens
  • ID - Hundreds
  • Inline Styles - Thousands

The selector h2 has a specificity score of 1, while the selector #left-column h2 has a score of 101. So, even if #left-column h2 is declared before h2, styles in #left-column h2 will take precedence.

A more complicated example might be to compare the following:

Assuming this HTML:

  1. [geshifilter-code]<div id="content" class="page">
  2. <div id="left-column">
  3. <h2 class="red">My Header</h2>
  4. </div>
  5. </div>
  6. [/geshifilter-code]

and this CSS:

  1. [geshifilter-code]#content #left-column h2 {
  2. font-size: 24px;
  3. color: #900;
  4. }
  5. body div.page div#left-column h2.red {
  6. font-size: 64px;
  7. color: #090;
  8. margin: 20px 0;
  9. }
  10. [/geshifilter-code]

Plenty of developers will assume that because the second selector seems more specific and that it's declared last that the header tag will appear big and green. However, the header will be 24 pixels and Georgia Bulldog red.

This is because the first select has a higher specificity score. The first one has two IDs and an HTML element; for a score of 201. The second one has 4 HTML tags, a class, and an ID; for a score of 114.

However, it's also important to note that the header will have a 20 pixel vertical margin, since the first selector didn't declare any margin. Just because a style has a lower specificity score doesn't mean it's completely ignored.

Tobby Hagler

Tobby Hagler

Director of Engineering