CSS Style Precedence
Learning outcomes:
- What is style precedence in CSS
- The specificity of selectors
- Specificity and the
style
attribute - Factors that affect style precedence
- The
!important
keyword
Introduction
It commonly occurs in CSS that multiple rules with identical properties apply to the exact same element.
For instance, we might have two stylesheets linked to an HTML page, each applying a color to the selector p
. Now obviously, the selectors in both the stylesheets match the exact same set of elements in the HTML document, so this is an instance where the same property, i.e. color
, is assigned to the same element via different rules.
The question is: Which one to choose?
Certainly, CSS has to use one property only and this is where the idea of style precedence kicks in. When faced with the situation of colliding style declarations from different rules, CSS chooses the one that has the highest precedence.
In this chapter, we shall learn about the various factors that affect the precedence of a style declaration. In the discussion, we'll also consider a new concept in CSS, that is, the !important
keyword.
Style precedence is one of the most fundamental topics in CSS, yet one that does require a little bit more practice to get the hang of. Understanding the factors that affect precedence is really helpful because, in some instances, it can allow us to control which styles apply to which elements and which not.
What is style precedence?
Before we get to unravel the intuition behind style precedence, it's obviously necessary to know exactly when does it kick in.
Likewise, let's first see a simple example of it in action.
Consider the following HTML:
<p id="p1">Paragraph 1</p>
<p>Paragraph 2</p>
We have two <p>
elements, with the first one additionally having an id
attribute. Nothing really too complex here.
Now suppose we have the following CSS:
p {
color: blue;
}
Trivially, this being the only rule applying color to the paragraphs, the paragraphs would be colored blue.
Now, suppose we add a rule for #p1
:
p {
color: blue;
}
#p1 {
color: red;
}
What do you think would happen now?
#p1
element? Blue or red? And why?Well, the correct answer is red. It's not too difficult to reason why.
The selector for the first rule, i.e. p
, is generic — it applies to all <p>
elements. In contrast, the #p1
selector for the second rule is very specific — it only applies to one element.
By virtue of this higher specificity of the ID selector, just by plain reasoning, we establish that the color
declaration in the second rule above, belonging to #p1
— that is, color: red
— should be applied to the #p1
paragraph.
In other words, the color: red
declaration has a higher precedence as compared to color: blue
, all thanks to the higher specificity of the ID selector, #p1
, against the type selector, p
.
This is just one instance where two (or more than two) identical declarations collide with each other, demanding CSS to choose one out of them.
But, as you can agree, there are many more such possibilities in a given document. Just think about the sheer amount of variations we could have in a selector — there are tons and tons of them.
And that's where CSS needs a more formal approach to deciding which particular declaration to pick.
The 'cascade' in CSS
The term 'cascade' in Cascading Style Sheets arises from the fact that this styling language is intrinsically based on the idea of styles overriding (or succeeding) other similar styles, based on a given set of conditions, when they apply to similar elements.
In other words, similar styles applied to the same element essentially go through a 'cascading' effect, whereby some styles take over and win the race of ultimately being selected for the final application.
It's because of this cascading nature of CSS that we could have multiple style sheets and then not worry about a given style sheet taking over completely; instead, all the style sheet blend together.
It's customarily the case that multiple style sheets target the same properties of the same elements, and thanks to the effect of cascading, only the most specific styles win.
Selector specificity
Before we go through the factors that affect the precedence of a given style declaration over identical, competing ones, it's worthwhile to first discuss the notion of selector specificity.
Well, based on the example above comparing the ID selector with the type selector, you'll probably be able to roughly describe specificity already:
The higher the specificity of a selector, the more specific it is and, thus, the higher the precedence of a style declaration tied to that selector.
For example, reviewing the previous example, the #p1
selector's specificity was higher than that of p
; hence, the color
declaration in the rule for #p1
took precedence over the one in the rule for p
.
There are two things that affect the specificity of a selector as discssed up next.
The kind of the selector
As you already know, some selectors in CSS are very generic whereas some are very specific.
Compare the type selector p
with the ID selector #p1
, for instance. Clearly, the ID selector is more specific than the type selector.
Therefore, the kind of the selectors we use governs their specificity.
CSS assumes the following specificity for the various kinds of selectors, in decreasing order:
- ID selectors
- Class selectors, attribute selectors, pseudo-classes
- Type selectors, pseudo-elements
- Universal selector (
*
)
Earlier selectors have higher specificity than later ones; selectors on the same line have the same specificity.
Why do some kinds of selectors have the same specificity?
You might wonder why class selectors, attribute selectors, and pseudo-classes have the same specificity, do you?
Well, this is because class selectors are just a special kind of attribute selectors — in other words, class selectors are, more or less, the same thing as attribute selectors.
As for pseudo-classes, the name tells it all — pseudo-classes are much like real classes of elements in HTML. Likewise, it makes sense to treat pseudo-classes in the same way as class selectors (which, as we learned, are treated the same as attribute selectors).
A similar reasoning could be applied to understand why type selectors and pseudo-elements have the same specificity.
As the first example, consider the following:
<p class="para" id="p1">A paragraph</p>
#p1 {
color: red;
}
p {
color: blue;
}
The color of the paragraph here would be red, not blue even though the color: blue
declaration exists later in the CSS (as we shall find out soon, later declarations have a higher precedence over earlier ones):
A paragraph
This is simply because the specificity of color: red
by virtue of the ID selector #p1
is greater than that of color: blue
.
However, if we change the CSS to the following, replacing the ID selector with a class selector, and the type selector with an attribute selector:
.para {
color: red;
}
[class="para"] {
color: blue;
}
we'll get the blue color applied and that's because color: blue
appears later in the code and because both the selectors .para
(class selector) and [class="para"]
(attribute selector) have the same specificity:
A paragraph
The number of individual selectors
In addition to the kind of selectors used, the more individual selectors we have in a complex selector, the higher its specificity.
Once again, this makes perfect sense.
Styles applied using the selector p.para
should ideally take precedence over identical styles applied using just .para
. This is because p.para
is certainly more specific than .para
in that it selects all <p>
elements that have the class "para"
.
Let's take a quick example:
<p class="para" id="p1">A paragraph</p>
p.para {
color: red;
}
.para {
color: blue;
}
The color applied to the paragraph here would be red and that's all because the selector corresponding to color: red
is p.para
which has a higher specificity than .para
.
A paragraph
Augmenting this example, now consider the following:
p.para {
color: red;
}
p.para {
color: blue;
}
Everything is the same as before except for that the .para
selector (with the declaration color: blue
) is now additionally prefixed with the type selector p
(as even highlighted).
What will the color of the paragraph be here?
The correct answer is blue and that's because this time both the selectors p.para
have the same specificity, and so the winner comes down to the declaration that appears later in the code. And that is clearly color: blue
.
A paragraph
Going along these very lines, we can consider tons and tons of examples with complex selectors, containing many individual selectors, and try to figure out the specificity of the selectors by the rules stated above.
However, this may not be that trivial without any well-defined approach.
For example, consider the following two selectors: p.para.intro
and .para.intro:hover
. Which one has a higher specificity and why?
Fortunately, CSS does have a well-defined approach to compute a selector's specificity, as mentioned in the specification.
Let's see what it is...
Computing a selector's specificity
The idea is that a given selector is assigned a set of three individual integers that represents its specificity. Here's how this set is computed:
Consider a complex selector s
and three integers a = 0
, b = 0
and c = 0
which represent the specificity of s
, to start with:
- For each ID selector in
s
, add 1 toa
. - For each class, attribute, and pseudo-class selector, add 1 to
b
. - For each type and pseudo-element selector, add 1 to
c
.
*
) doesn't contribute to the specificity.In the end, when comparing two selectors for specificity, go through each a
, b
, and c
in turn.
When a mismatch is found, the selector with the larger integer (a
, b
or c
) has a higher specificity. Otherwise, the selectors have the exact same specificity.
With all this knowledge in place, let's sort out some pairs of selectors for their winners.
The competition begins! 🥁
First, let's deal with the question stated right before the beginning of this section: Which of the two selectors, p.para.intro
and .para.intro:hover
, is more specific?
p.para.intro | .para.intro:hover |
---|---|
There is no ID selector, so a = 0 . | There is no ID selector, so a = 0 . |
There are two class selectors, so b = 2 . | There are two class selectors and one pseudo-class selector, so b = 3 . |
There is one type selector only, so c = 1 . | There are no type or pseudo-element selectors, so c = 0 . |
Thus, the winner is .para.intro:hover
because b = 3
in it is larger than b = 2
in p.para.intro
.
Now, let's determine the winner in .btn:hover
and body main button:hover
.
.btn:hover | body main button:hover |
---|---|
There is no ID selector, so a = 0 . | There is no ID selector, so a = 0 . |
There is one class selector and one pseudo-class selector, so b = 2 . | There is one pseudo-class selector only, so b = 1 . |
There are no type or pseudo-element selectors, so c = 0 . | There are three type selectors only, so c = 3 . |
And so the winner is .btn:hover
because b = 2
in it is greater than b = 1
in the selector body main button:hover
.
Most importantly, this example illustrates the fact that a longer complex selector doesn't necessarily mean that it has a higher precedence; its the kind of the individual selectors that matters along with the number of selectors.
That is, body main button:hover
is comprised of 4 selectors, so to speak, however it still has a lower specificity than .btn:hover
and that's by virtue of the more dominant class selector in .btn:hover
.
Alright, with selector specificity understood well enough, let's understand a similar idea of specificity of the style
attribute.
Specificity and the style
attribute
One weird thing with respect to selector specificity is that the official CSS spec treats the style
attribute in the same light as it treats selectors.
That is, when computing the specificity of a selector, the steps for the computation also consider the case whether there is no selector, i.e. when we're within the style
attribute.
I find this a little bit counter-intuitive. When we're computing the specificity of a selector, it doesn't make sense to also include the style
attribute in there and treat it as a 'no-selector' construct.
That's why I tend to take a different but theoretically similar approach.
In this way, we could easily define the specificity of a style declaration to depend upon the selector corresponding to it or, if there ain't one, on the fact that it appears in the style
attribute.
In particular, when a style occurs in the style
attribute, it has the highest specificity by virtue of the attrbute.
Simpler, isn't it?
Now, in the same way, we reasoned about a
, b
, and c
above, we can extend to four numbers — a
, b
, c
, and d
— instead of three. a
would then deal with style
, and the rest of the numbers would deal accordingly (as we learned above).
Based on this, let's compute the specificity of the two color
declarations shown below which apply to the <p>
element.
<p style="color: red" id="p1">A paragraph</p>
#p1 {
color: blue;
}
Here's the comparison:
color: red | color: blue |
---|---|
It's in the style attribute, so a = 1 . | It's not in the style attribute, so a = 0 . |
b = 0 . | The selector #p1 contains one ID selector, so b = 1 . |
c = 0 . | The selector #p1 does not contain any class, attribute, or pseudo-class selectors, so c = 0 . |
d = 0 | The selector #p1 does not contain any type or pseudo-element selectors, so d = 0 . |
The specificity of color: red
is clearly greater than that of color: blue
because a = 1
for color: red
but a = 0
for color: blue
. And that's why color: red
wins.
A paragraph
So now that we know about the paramount concept of specificity, it's time that we finally get to learn about the factors that affect style precedence.
Factors that affect precedence
There are many factors that contribute to the winning streak of a given style declaration amongst many others. These are discussed one by one below.
The location of the declaration
In general, a later declaration takes precedence over a previous (identical) declaration, given that they have the same specificity.
For example, consider the following rule:
p {
color: blue;
color: red;
}
The color chosen by CSS here would be red
and that's simply because color: red
appears later in the code as compared to color: blue
.
A paragraph
As another example, consider the following:
p {
color: green;
}
p {
color: red;
}
The color of the paragraph here would also be red
because the rule for p
with color: red
appears later in the code:
A paragraph
The specificity of the declaration
The higher the specificity of a declaration, the higher its precedence.
As a familiar example, take a look at the following:
<p class="para" id="p1">A paragraph</p>
#p1 {
color: red;
}
p {
color: blue;
}
The ID selector here wins over the type selector and so does its color: red
style despite the fact that the color: red
declaration comes earlier in the style sheet than color: blue
(associated with the type selector p
).
A paragraph
Elaborating upon it, the later appearance of color: blue
in the source code doesn't get it to win over color: red
because its specificity, due to the selector p
, is lower than that of color: red
, due to the selector #p1
.
The !important
keyword
By far, the most dominant declaration in a series of colliding declarations is achieved using the !important
keyword.
As its name suggests:
!important
marks a style declaration as important, that is, it assigns it the highest precedence.If a declaration is flagged as !important
, it wins over all other declarations, be they in the style
attribute, or within rules laid out using ID selectors, or anything of that sort.
!important
is immediately added after a property's value. The general syntax is expressed as follows:
property: value !important;
!important
keyword appears before the semicolon (;
) that terminates the given declaration.An example will help clarify the intuition behind !important
.
In the following code, we have a color style inside the style
attribute of the paragraph and a style in the style sheet, applied using the ID selector #p1
:
<p id="p1" style="color: red">A paragraph</p>
#p1 {
color: blue;
}
By now, we know that the color of the paragraph would be red, by virtue of the application of color: red
in the style
attribute.
A paragraph
However, let's say that some third-party script adds this <p>
element to the page and so we don't have control over its style
attribute, or over the <p>
element itself, and we now wish to change its color to blue.
How to do so?
Well, to override styles in the style
attribute, we have the provision of the extremely powerful !important
keyword in CSS.
The basic idea is that because we wish to have the color: blue
style here take precedence over the color: red
style in the style
attribute, we can flag color: blue
as !important
.
Something like this:
#p1 {
color: blue !important;
}
A paragraph
As can be seen above, now the blue color gets applied. The color: blue
declaration with the advent of !important
takes the highest precedence and wins over the color: red
declaration in the style
attribute.
!important
is a powerhouse!
The only thing that can win over an !important
declaration is another !important
declaration that has a higher precedence without the !important
keyword.
Let's see what this means.
Going with the example above, if we wish to now color the paragraph green by selecting it with another #p1
selector, we can flag it as !important
too.
Something as follows:
#p1 {
color: blue !important;
}
#p1 {
color: green !important;
}
A paragraph
Here, the newly-added color: green
declaration wins over the earlier one because of two reasons:
- First, because the declaration otherwise has a higher precedence as compared to the previous
color: blue
declaration if we ignore the!important
flags. - Second, because it also has the
!important
flag set and could therefore compete with the earlier!important
declaration.
Had the new selector been p
instead of #p1
, the green color wouldn't have been applied. This is illustrated below:
#p1 {
color: blue !important;
}
p {
color: green !important;
}
A paragraph
This is because from both the declarations that have !important
set, clearly the color: blue
declaration has a higher precedence by virtue of its selector #p1
compared to p
for color: green
.
!important
set, as in the example above, the winner's selection comes down to those declarations that have a higher precedence without the !important
keyword.Spread the word
Think that the content was awesome? Share it with your friends!
Join the community
Can't understand something related to the content? Get help from the community.