In this article, you will learn how to calculate CSS specificity of the styles you write by computing a compound number to measure specificity weight by CSS selector.
First off,
Specificity is the algorithm browsers use to establish the CSS declarations that will get applied to an element, when it is referenced by two or more style rules.
Specificity algorithm calculates weight of CSS selector, and among competing selectors targetting one element, the selector with the greatest weight will win to apply CSS declarations to that element.
Visual definition of CSS terms
The 3-column value
The specificity algorithm bases weight calculation around a three-column compound number. Each column represents weight that correspond to the three types of CSS selectors, i.e ID, CLASS and TYPE. This three-column compound number starts off with zeroes looking like:
- ID column - Includes only ID selectors, such as
#app
. For each ID in a matching selector, add[1, 0, 0]
to the weight value. - CLASS column - Includes class selectors, such as
.myInput
, attribute selectors e.g[type="password"]
, and pseudo-classes such as:hover
,:first-of-type
, e.t.c. For each class, attribute selector, or pseudo-class in a matching selector, add[0, 1, 0]
to the weight value. - TYPE column - Includes type selectors, such as
p
,h2
, anda
, and pseudo-elements like::before
,::placeholder
, and all other selectors with double-colon(::
) notation. For each type or pseudo-element in a matching selector, add[0, 0, 1]
to the weight value.
Specificity comparison
Let's look at the following rule, which is composed of 7 selectors to target a span
element:
#app #navigation #list .sub-list .item p span {
/* declarations here */
}
There are 3 ID selectors(#app
, #navigation
, #list
). So the three-column value becomes [3, 0, 0]
.
Then 2 class selectors(.sub-list
, .item
). The three-column value compounds to [3, 2, 0]
.
Then 2 type selectors(p
, span
). The three-column value compounds to [3, 2, 2]
.
Now provided the number in each column is 9
or less, we can concatenate the numbers in each column to get a base10 number as the resultant specificity weight. So in this case we have a calculated specificity weight as 322
.
Disclaimer: This is not how specificity is calculated by CSS processor inside browsers, but only a blue print. Normally in browsers, the specificity is not calculated in base 10 number system, but in a larger number, often unspecified.
Suppose we have another rule styling the same span
element looking like this:
#navigation #list .sub-list .item .wrapper p span {
/* declarations here */
}
Likewise in the style rule above, 7 selectors are used to target the span
element. Difference being that we have 2 ID selectors and 3 class selectors, which results in the three-column value [2, 3, 2]
. Hence a specificity weight 232
. Because 322
is greater than 232
, the former has precedence over the latter. Therefore, the former style rule will be processed to style the span
element.
Selectors that add no weight to specificity calculation
The universal selector *
and the pseudo-class :where()
with its parameters do not add onto specificity weight so their weight is [0, 0, 0]
; but they do match elements.
Combinators, such as +
,>
, ~
, " "
, and ||
, may make a selector more specific in what is selected but they don't add any weight to specificity.
:not()
, :has()
and :is()
pseudo classes add no weight by themselves. However their parameters do impact weight in specificity calculation. Therefore, the weight calculated from the selectors, is inclusive of the weight from the parameters of these pseudo-classes.
Incase of a parameter list, weight by the parameters with the highest specificity is used. For example:
/*
<span id="intro" class="lead">...</span>
*/
span:is(.lead, #intro) {
/* calculated specificity - [1, 0, 1] */
}
In the above snippet, inside the :is()
parameter list, #intro
has higher weight ([1, 0, 0]
) than .lead
([0, 1, 0]
) . Hence weight by #intro
is used to sum up with weight by the span
selector resulting to [1, 0, 1]
.
Example
Consider the following HTML:
<html>
<head>
...
</head>
<body>
<main id="myElement">
<input type="password" required class="myInput" />
</main>
</body>
</html>
In the below CSS, three rules are targeting <input />
element to set a color. For a given input, color
value is applied should its selectors specificity weight have precedence over the other matching selectors.
/* FIRST RULE */
#myElement input.myInput {
color: red;
} /* [1, 1, 1] */
/* SECOND RULE */
input[type="password"]:required {
color: blue;
} /* [0, 2, 1] */
/* THIRD RULE */
html body main input {
color: green;
} /* [0, 0, 4] */
All the above rules have selectors matching the same and only <input />
element on our HTML. The input will have a color red
because the first rule has the highest specificity weight, i.e 111
is the greatest number from the weights of 111
, 021
and 004
.
The last rule has 4 type selctors(html
, body
, main
, input
). Increasing even more type selctors will still result in <input />
being color red
since specificity weight by type selector is always lower than ID selector.
Excercise
Consider the same html to deduce the result of the below CSS:
.myInput {
border: 0;
}
:is(input.myInput) {
border: 1px solid black;
}
:where(#myElement input[type="password"]) {
border: 4px solid blue;
}
What will happen to border on the <input />
element?
Share your answer in the comments👍️.
A Column number that is greater than 9
I had mentioned earlier that to get the specificity weight you concatenate the column values to get a number like 322
. This is because for less than 10 selectors of a type(either ID, Class or Type), we can calculate the specificity in the base 10. Hence concatenating, we can get a number in the base10 number system.
This is not the case when we have 10 and above selectors of a type. High number of selectors can occur when using nesting feature of css preprocessors like sass to generate css. You can end up with three-column value like [11, 7, 19]
. You can't concatenate the numbers to obtain a decimal specificity weight in this case.
But instead you can convert the column numbers to a higher base.
For example, lets say we have a three-column value of [11, 7, 19]
. To calculate the weight, we can pick a base number that is larger than the largest column number in the three-column value.
Let's pick a base number 20
which is bigger than 19
(largest number in [11, 7, 19]
). Then multiply out the three columns starting rightmost working to the left. And add the results, like shown:
(20) * 19 = 380
(20*20) * 7 = 2 800
(20*20*20) * 11 = 88 000
-------------------------
Total in decimal = 91 180
Doing this will also require you to use that base number to calculate specificity weight for the other competing selectors. Thankfully, CSS processor handles all this for you. so you just have to understand "why a certain style has been applied and not the other".
Equivalent weights
Where 2 or more style rules have exactly the same specificity weight, the most recent rule will take precedence. However you can force a rule to a higher precedence over other equivalent rules using !important
keyword like:
p {
color: #00ff00 !important;
}
When you do this, all previous style rules with equivalent weight are overridden(including ones using !important
) and any equivalent rules that are processed will be ignored.
For example:
p {
color: #00ff00 !important;
} /* [0, 0, 1] */
p {
color: #ff0000;
} /* [0, 0, 1] */
Both rules have the same weight but the first rule will take precedence because of use of !important
keyword. Otherwise, the second rule would have taken precendence.
Conclusion
CSS has a simple syntax with alot happening behind scenes with the CSS processor. We have talked about specificity in this article, but specificity only comes to play after browser has determined cascade origin and importance. You can explore more which is beyond scope of this article.
In a nutshell, knowing specificity of your styles helps you construct rules and understand what precedence they will have.
We have not talked about inline styles, which are the most specific, meaning that when an inline style is applied, it has the highest specificty overriding any style in the style sheet. The only way to override an inline style from a style sheet is to use !important
keyword.
Meet me aside on
I periodically share useful content you may not want to miss.
Top comments (4)
Hi Smitter,
A very well prepared and informative article. Thank you.
I think CSS specificity is actually calculated from 5 going on 6 factors. I remember them using the mnumonic IS-ICE
I have a demonstration on CodePen.
The 6th factor, which I have yet to utilise myself is @layer, which have a scoping effect.
Regards, Tracy
Yes the factors you have mentioned are totally right. In this article I was abit more focused on how to calculate the weight as a number that causes a style to be applied instead of the other. Acording to the mnemonic
IS-ICE
, i may say the article has explained in detail on theICE
part. Towards the end, I have touched lightly onIS
part:Where I mean that the most recently applied style wins, BUT if only the previous style does not have an
!important
keyword.Regarding the
@layer
, When you don't specify a layer in your stylesheet, the CSS processor will place your styles(those not in a layer) into an anonymous layer. Now styles in anonymous layer have a higher specificity weight than a custom layer you define on your stylesheet. A good usecase can be to put bootstrap styles in a layer you have created.And your own styles you write not enclosed in a layer, or in a layer you have created more recently. This causes CSS processor to always apply your styles overriding what bootstrap has set.
Thanks for the mnemonic, It makes it easy to remember
Сongratulations 🥳! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up 👍
thank you