CSS specificity is one of the mechanism used by the browser to resolve conflicts in your CSS rules. Now you might be thinking: conflict? What conflict?
Given the code snippet below, which particular CSS rule do you think will get applied to the HTML paragraph?
<div>
<p id="paragraph">Hello world, welcome to frontend zero to hero</p>
</div>
p {
font-size: 120%; /* option one */
}
div p {
font-size: 0.5em; /* option two */
}
#paragraph {
font-size: 10px; /* option three */
}
Taking a closer look, you'll notice that we are trying to change the font-size
of the paragraph with three different rules in our CSS. That's the conflict I was talking about. But which rule will the browser use? Take a guess.
Option three will win. Why option three? When you give such rules, the browser will use one of its a mechanism in resolving conflicts in CSS rules — specificity. In this case, the browser will use the rule declared with the id selector to style the paragraph. How could this be? To answer this question, let's find out how the browser calculates a selector specificity.
CALCULATING A SELECTOR SPECIFICITY
From the CSS Specification:
A selector's specificity is calculated as follows:
- count 1, if the declaration is from, is a 'style' attribute rather than a rule with a selector, 0 otherwise (= a) (In HTML, values of an element's "style" attribute are style sheet rules. These rules have no selectors, so a=1, b=0, c=0, and d=0.)
- count the number of ID attributes in the selector (= b)
- count the number of other attributes and pseudo-classes in the selector (= c)
- count the number of element names and pseudo-elements in the selector (= d)
At first glance this might be confusing, let's explain each step of the algorithm.
count 1 if the declaration is from is a 'style' attribute rather than a rule with a selector, 0 otherwise (= a) (In HTML, values of an element's "style" attribute are style sheet rules.
This translates to: When you apply some styling to an HTML Element using the style
attribute it will have a value of 1
and all property declaration in this style
attribute will be used by the browser even if you declare the same set of properties with other selectors (id
,class
or type selectors
).
Let's use our previous example with a slight modification. Save the HTML code in a file with the .html
extension and the CSS with a .css
extension, and make sure the CSS is linked with the HTML (if you don't know how to do this, please check the previous article CSS units)
<div>
<p id="paragraph" style="font-size: 20px">Lorem ipsum dolor sit
amet, consectetur adipisicing elit,.</p>
</div>
p {
font-size: 120%; /* option one */
}
div p {
font-size: 0.5em; /* option two */
}
#paragraph {
font-size: 10px; /* option three */
}
Load the file in your browser, Use "Inspect Element" on the paragraph, you will get an output similar to the image below:
From the image above, if we observe the Computed tab, we'll see that the browser is using 20px
as the font-size
.
This clearly indicates that property declarations in the style
attribute takes precedence over the same set of property declarations declared with an id
, classes
, pseudo-selectors
and plain tag selectors
. That's why it's given a value of 1
and the specification assign it the letter a
.
count the number of ID attributes in the selector (= b)
After the style
attribute, property declaration in an id
attribute takes precedence over the same set of property declarations in other attributes and elements as demonstrated at the beginning of this post.
id's
are given the number 0
and assigned the letter b
in the specification.
count the number of other attributes and pseudo-classes in the selector (= c)
This tells us that classes
, attribute selectors
and pseudo-class
takes precedence over type selectors
and pseudo-elements
. Let's take an example.
<div>
<p class="paragraph">Lorem ipsum dolor sit amet, et dolore magna aliqua.</p>
</div>
/* property declaration with one tag (div) and a pseudo-selector (:first-child)*/
div:first-child {
font-size: 50px;
}
.paragraph {
font-size: 120px;
}
Load the file in your browser and use "Inspect Element" in the paragraph. You will realize the browser opted to apply the property declared with the class
. They are assigned the number 0
and the letter c
in the specification.
count the number of element names and pseudo-elements in the selector (=d)
Pseudo-elements and tag selectors are last in the chain they are assigned the number 0
and letter d
in the specification.
In summary:
-
style
: assigned number1
and lettera
-
id
: assigned number0
and letterb
-
classes
,attribute selectors
,pseudo-classes
: assigned number0
and letterc
-
type selectors
andpseudo-elements
: assigned the number and letterd
The default number assigned to this selectors in the specification can be summed up as: 1
, 0
, 0
, 0
Now you might think: will the numbers assigned to these selectors ever change? Yes, except that of the style
attribute, it will always be 1
because HTML does not allow duplicate attributes applied to an element and if you make such a mistake, the browser will use the first one and gladly ignore the rest.
Take the code sample below, multiple font-size
declarations are applied to the paragraph but only the first is applied by the browser.
<div>
<p style="font-size: 50px;" style="font-size: 100px;" style="font-size: 200px;">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
</div>
And if you use viewsource
on your code, you'll see the error highlighted in red.
For the remaining selectors, their number increase based on the number of times they are applied to an element, but the selector higher in ranking will always take precedence irrespective of the number of times you apply any selector that's is lower in precedence. Given the code snippet below:
<div>
<p id="lot-of-ps" class="p1 p2 p3 p4">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, aliqua.</p>
</div>
#lot-of-ps {
font-size: 10px;
}
.p1.p2.p3.p4 {
font-size: 40px;
}
In this case, font-size
declared with the id
selector will win because it has a higher specificity than all the classes combined. Which gives the following number 0
, 1
, 4
, 0
-
0
- we did not use astyle
attribute, so it gets a zero -
1
- we had oneid
selector, hence the number one -
4
- we applied fourclass selectors
-
0
- we did not use anypseudo-element
selector, so it gets a zero
Now, what happens if we have no id
selector? Take the code snippet below, which CSS selector wins?
<div>
<p class="p1 p2 p3 p4">Lorem ipsum dolor sit amet, ut labore et dolore aliqua.</p>
</div>
.p1.p2.p3.p4 {
font-size: 40px;
}
div p.p1 {
font-size: 100px;
}
The first CSS selector will win because it has a specificity value of 0
, 0
, 4
,0
:
-
0
- nostyle
attribute selector -
0
- noid
selector -
4
- fourclass
selectors -
0
- we used twotag selector
div
andp
TOOLS FOR UNDERSTANDING CSS SPECIFICITY
Keegan Street Created a tool CSS Specificity Calculator, it automatically shows the Specificity of your declaration when you type (or paste) in your CSS declaration.
Andy Clarke has a poster illustrating CSS specificity using characters from Star Wars.
Estelle Weyl co-author of CSS The Definitive Guide Visual Presentation for the Web Fourth Edition created specifishity.
Now, what happens when the selectors have the same specificity?
/*0012*/
div p.p1 {
font-size: 100px;
color: red;
}
/*0012*/
div p.p1 {
font-size: 200px;
color: orange;
}
Which selector will the browser use? That's when the 'C' in CSS comes into play, The Cascade.
That is next.