Get Specific with Your CSS Styles
Other than being the C in the acronym CSS, the fact that style sheets are described as "cascading" refers to an important, if complex, part of the way styles are applied to the elements in a document. It’s called the CSS cascade because style declarations cascade down to elements from many origins.
The cascade combines many factors in order to determine exactly — and without conflict — which declaration should be applied to any given element. The factors influencing whether a style rule takes effect or not are the importance, origin, source order, and (possibly the most misunderstood, and poorly documented factor) specificity.
Specificity is a mechanism within the CSS cascade that aids conflict resolution. The concept of specificity states that when two or more declarations that apply to the same element, and set the same property, have the same importance and origin, the declaration with the most specific selector will take precedence.
Consider this example:
p {
color: black;
background-color: white;
}
div.warning p {
color: red;
}
div#caution p {
color: yellow;
}
body#home div p {
color: white;
}
The above example style sheet contains four style rules that have a selector that matches p
elements. Because one of those rules has an element type selector p
, it’s guaranteed that two or more rules will apply to the same p
element, and because they all contain a color
property declaration, the user agent needs a way to determine which of the declarations should be applied. What will the final color
value be for the p
element?
The simple answer is that the more specific selector’s declaration will take precedence. The user agent calculates each selector’s specificity so that a comparison can be made, and resolves the deadlock by choosing the declaration whose selector has the highest specificity.
This article is taken from SitePoint’s The Ultimate CSS Reference, which is available free online, and can be purchased in hard-cover print format.
Calculating Specificity
Here’s a simplified description of the process by which the specificity of the selectors of two or more declarations is compared:
- If one declaration is from a
style
attribute, rather than a rule with a selector (an inline style), it has the highest specificity. If none of the declarations are inline, proceed to step two. - Count the ID selectors. The declaration with the highest count has the highest specificity. If two or more have the same number of ID selectors, or they all have zero ID selectors, proceed to step three.
- Count the class selectors (for example,
.test
), attribute selectors (for example,[type="submit"]
), and pseudo-classes (for example,:hover
). The declaration with the highest total has the highest specificity. If two or more have the same total, or they all have totals of zero, proceed to step four. - Count the element type selectors (for example
div
) and pseudo-elements (for example,:first-letter
). The declaration with the highest total has the highest specificity.
If two or more selectors have the same specificity, then, according to the rules of the CSS cascade, the latter specified rule takes precedence.
If you want to be technical, the W3C recommendation (6.4.3) describes the method for calculating a selector’s specificity. The result of this calculation takes the form of four comma-separated values, a,b,c,d
. (This is different from the CSS1 specification, in which specificity took the form of a number score, as explained at https://www.w3.org/TR/CSS1#cascading-order.) Here, the values in column "a
" are the most important and those in column "d
" are least important. A selector’s specificity is calculated as follows:
- To calculate
a
, count 1 if the declaration is from a style attribute rather than a rule with a selector (an inline style), 0 otherwise. - To calculate
b
, count the number of ID attributes in the selector. - To calculate
c
, count the number of other attributes and pseudo-classes in the selector. - To calculate
d
, count the number of element names and pseudo-elements in the selector.
The result of counting these elements is not a score, but a matrix of values that can be compared column by column. As an example, consider the following rule which contains an element type selector from the previous example:
p {
color: black;
background-color: white;
}
If we try to work out the specificity of the above selector on the basis of the specificity formula, we arrive at a result that looks like 0,0,0,1
, as it has one element name.
As we said before, this is not a number but four comma-separated values, where the values in column a (inline styles) are the most important, and those in column d (element names and pseudo-elements) are the least important. When comparing selectors to determine which has the highest specificity, look from left to right, and compare the highest value in each column. So a value in column b will override values in columns c and d, no matter what they might be. As such, specificity of 0,1,0,0
would be greater than one of 0,0,10,10
.
Specificity Step by Step
Let’s try and break down each part of the calculation procedure so that it’s more understandable.
The first step is to calculate the value for column a
, which we’ve done in Table 1. If the style rule is specified within the element’s HTML style attribute, a
should equal 1
; otherwise, it should equal 0
. In fact, this is the only case where there is a value in column a
.
As you can see, an inline style rule will always have a specificity of 1,0,0,0
— the highest level of specificity. Here’s an example of such a style rule:
<p style="color:red;">Red Text</p>
This is one of the reasons why inline styles should be avoided. As inline style rules always have the highest specificity, the only way to overwrite them within the CSS cascade is to use the !important
statement on the relevant declarations — an approach that creates a maintenance nightmare.
For rules other than inline styles, we need to calculate columns b
, c
, and d
. Let’s run through a full calculation for the following rule:
body#home div#warning p.message {
color: red;
}
The above rule has a selector, body#home div#warning p.message
, and a single declaration, color: red;
. Since this isn’t an inline style, we start off with a 0
in the first column, as Table 2 shows.
To calculate the value for column b
, we count the number of ID selectors in the selector. In our selector, body#home div#warning p
, there are two — #home
and #warning
— thus, column b
is equal to 2
, as is depicted in Table 3.
Next, we calculate the value for column c
, counting the number of class selectors, attribute selectors, and pseudo-classes in the selector.
Attribute Selectors for IDs
Note that [id="foo"]
is not equivalent to #foo
— you can see there’s a significant difference in their levels of specificity.
In our example selector, body#home div#warning p.message
, there’s one class selector, .message
, so, as you can see in Table 4, c
is equal to 1
.
Finally, for column d
, we count the number of element type selectors and pseudo-elements in the selector. In our example selector, body#home div#warning p.message
, there are three: body
, div
, and p
. There are no pseudo-elements to be counted, so we put a 3
in the last column, as Table 5 shows.
We now have our result. The specificity for the selector body#home div#warning p.message
can be expressed as: 0,2,1,3
.
All right, let’s consider a crazy situation where more than half a dozen color declarations for the same p
element have the same levels of importance and origins. Which color would the browser apply to the element?
Here’s our crazy style sheet:
p.message {
color: green;
}
#home #warning p.message {
color: yellow;
}
#warning p.message {
color: white;
}
body#home div#warning p.message {
color: blue;
}
p {
color: teal;
}
* body#home>div#warning p.message {
color: red;
}
#warning p {
color: black;
}
We should be able to use the specificity calculation method to work out which of the declarations would be applied. But, wait a minute! What are the levels of specificity of the universal selector, *
, and the child combinator, >
?
The answer is that they don’t have any specificity at all; they’re simply ignored in all calculations. This is true for all combinators, which you can treat as though they had a specificity of zero, as they will make no difference to your calculation. After all, five out of the seven selectors above use the descendant combinator and you weren’t worried about those!
See if you can work out the specificity of all the selectors above for yourself before looking at the answer in Table 6.
The results have been ordered according to specificity — the highest are at the top, and the lowest are at the bottom. As you can see, the top two selectors have exactly the same specificity, despite the extra universal selector and combinator in one of them. In this case, they tie for specificity and the one that appears last in the style sheet will be the winner. If you look at the original style sheet source above, the red color will be applied to the p
element.
You can see from Table 6 that the selector p.message
has a lower specificity than the selector #warning p
. This is a common cause of head scratching among those new to CSS, who often think that a class selector will be specific enough to match an element in all cases.
To learn more about the CSS language, visit the online reference, and check out the hard-cover edition.