CSS: Foundation — Style Precedence

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:

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:

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:

CSS
p {
   color: blue;
}

#p1 {
   color: red;
}

What do you think would happen now?

Which color would be applied to the #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.

Recall that IDs are meant to be unique in an HTML document. So it won't be completely wrong to assume that an ID selector targets one single 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 specificity of a selector describes how specific that selector is.

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.

An ID selector is also a special kind of an attribute selector but it's given a higher specificity for an obvious reason — it's intrinsically meant to select one single element and likewise it rightfully demands a higher specificity.

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:

HTML
<p class="para" id="p1">A paragraph</p>
CSS
#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

Live Example

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:

CSS
.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

Live Example

As we learned above, class and attribute selectors both have the same specificity.

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:

HTML
<p class="para" id="p1">A paragraph</p>
CSS
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

Live Example

Augmenting this example, now consider the following:

CSS
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

Live Example

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 to a.
  • For each class, attribute, and pseudo-class selector, add 1 to b.
  • For each type and pseudo-element selector, add 1 to c.
The universal selector (*) 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:hoverbody 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.

We could take specificity to as well be a characteristic of a style declaration.

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.

HTML
<p style="color: red" id="p1">A paragraph</p>
CSS
#p1 {
   color: blue;
}

Here's the comparison:

color: redcolor: 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 = 0The 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

Live Example

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:

CSS
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

Live Example

As another example, consider the following:

CSS
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

Live Example

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:

HTML
<p class="para" id="p1">A paragraph</p>
CSS
#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

Live Example

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 location of style declarations in the source code only comes into consideration when the specificity of the selectors of those declarations is the same.

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;
The !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:

HTML
<p id="p1" style="color: red">A paragraph</p>
CSS
#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:

CSS
#p1 {
   color: blue !important;
}

A paragraph

Live Example

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:

CSS
#p1 {
   color: blue !important;
}

#p1 { color: green !important; }

A paragraph

Live Example

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:

CSS
#p1 {
   color: blue !important;
}

p { color: green !important; }

A paragraph

Live Example

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.

When multiple colliding declarations have !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.

Open Discord

 Go to home Explore more courses