Whenever a post about Specificity in CSS – and by extension the Cascade itself – gets published I get very excited as it’s a core concept of the language that everyone should know. The more articles on this, the better!
However, I also sometimes raise an eyebrow as from time to time I, unfortunately, encounter some parts that are not entirely correct of just outright wrong.
To remove some of the confusion, here’s a (short) list of misconceptions about Specificity in CSS …
~
# Table of Contents
- Misconception 1: Specificity is a decimal score
- Misconception 2: Using the style attribute adds Specificity
- Misconception 3: Using
!important
adds Specificity - Learn More
~
# Misconception 1: Specificity is a decimal score
Some, typically older, articles out there express Specificity as a decimal score, or mention that a thing like a class selector “adds 10 points”. This is inaccurate as it would imply that 11 element selectors with their “1 point each” would beat 1 class selector that only has “10 points”.
Instead, Specificity is a triple (or a triad) that has three components: A, B, and C. The values for A, B, and C depend on what type of selector you use.
A
= id-like specificityB
= class-like specificityC
= element-like specificity
It is often represented using the (A,B,C)
notation. For example: (1,0,2)
. The alternative A-B-C
notation is also commonly used.
🤓 I wrote a JavaScript library that can calculate the specificity of a Selector: @bramus/specificity
. It powers this interactive specificity calculator.
Specificities are compared by comparing the three components in order: the specificity with a larger A value is more specific; if the two A values are tied, then the specificity with a larger B value is more specific; if the two B values are also tied, then the specificity with a larger C value is more specific; if all the values are tied, the two specificities are equal.
In code, it looks like this:
const compare = (s1, s2) => {
if (s1.a === s2.a) {
if (s1.b === s2.b) {
return s1.c - s2.c;
}
return s1.b - s2.b;
}
return s1.a - s2.a;
};
For example, (1,0,0)
is a higher specificity than (0,10,3)
because the A
value in (1,0,0)
(which is 1
) is greater than the A
value from (0,10,3)
(which is 0
).
Also see this excerpt from my CSS Day 2022 talk about the cascade:
💡 While you could represent the triad as a decimal by adding enough leading zeroes, it comes with its own set of challenges: the number you end up with is hard to read/parse + you would need to add “enough” zeroes to get a correctly sortable result.
My advice here remains this: don’t.
Where the confusion comes from:
Back in CSS Selectors 3, the spec mentioned that you could concatenate the numbers to get the specificity.
Concatenating the three numbers a-b-c (in a number system with a large base) gives the specificity.
While it is caveated with “in a number system with a large base” the clause got ignored, most likely because the spec showed examples in base 10 – e.g. #s12:not(FOO) /* a=1 b=0 c=1 -> specificity = 101 */
.
Almost 15 years later and we are still stuck with it: some writers keep representing Specificity in base 10 because it’s “simpler to explain”. While that might be true, it’s teaching people the wrong thing. Having them unlearn something later on is much much harder to do.
~
# Misconception 2: Using the style attribute adds Specificity
I often read that using the style attribute “adds 1000 points of specificity”. This is not correct as evaluation of the style attribute is an earlier step of the cascade – it has nothing to do with Specificity at all.
Where the confusion comes from:
Back in the CSS2 days (in 2011) this was actually the case. In that spec, Specificity was quadruple (A,B,C,D)
, with the style attribute being the A component.
Since CSS3, this is no longer the case, though.
~
# Misconception 3: Using !important
adds Specificity
I often read that adding !important
to a declaration “adds 10000 points of specificity”. This is not correct as using !important
puts the declaration in a different origin. By using !important
in your author styles, the declaration moves from the Normal Author Declarations origin to the Important Author Declarations origin.
Origins and importance is the first criterion of the cascade that is checked. Specificity comes much much later.
Where the confusion comes from:
I couldn’t find any hard traces in the specifications, but my guess this was something someone came up back in the day when the Specificity directly followed the Origins criterion. That simplification did make sense back then but nowadays it no longer does as it skips over three extra criterions of the cascade that sit in between Origins+Importance and Specificity: Context, Element attached styles, and Layers.
~
# Learn more
To learn more about Specificity and the Cascade, I’d recommend you to watch the talk I gave a CSS Day 2022 that digs into all there is to know about it.
On the dedicated post here on bram.us you can also grab the slides.
~
🔥 Like what you see? Want to stay in the loop? Here's how:
Very concise explanation of specificity. It always feels like a guess and check game for me. Thanks!