Skip to main content

Command Palette

Search for a command to run...

CSS Specificity Algorithm: Why your styles do not always apply?

Updated
5 min read
CSS Specificity Algorithm: Why your styles do not always apply?

As Frontend Developers, we have faced the issue in some projects or maybe in every project where we want to apply a style to an element that NEVER applies. No matter how many times we inspect the element or tweak the CSS, the styles refuse to take effect.

Most of the time, the reason behind this frustrating issue is the Specificity Algorithm of CSS, where a more specific selector containing the same styles will take precedence and overrule what you are attempting to apply.

A Simple Example

Suppose you are working with the following HTML and CSS:

<button class="button"> Learn CSS Specificity </button>
.button {
    background-color: red;
}

button {
    background-color: blue;
}

There are two competing styles applied to the same element. One with a background-color: red; and another one with background-color: blue;. The question is which one of them gets applied?

At first glance, you might assume that the button's background will be blue maybe because you thought that since CSS applies styles in a top-down approach and as background-color: blue; appears later, it will override the previous one background-color: red;.

Well, let’s see how it looks in the browser.

Ah, the actual result is red.

Why is this happening?

This behaviour may seem confusing at first, but it all comes down to CSS specificity. In this blog, we will be demystifying why this strange behaviour occurs by breaking down how the Specificity Algorithm of CSS works and why certain styles take precedence over others.

What is specificity?

According to MDN, Specificity is the algorithm used by browsers to determine the CSS declaration that is the most relevant to an element, which in turn, determines the property value to apply to the element.

In the above example, since the class selector has more weight than the tag selector due to specificity, the property declared in the class selector got higher preference by the browser.

Now, if I comment out the styles applied using the class selector, you will see that the styles in the tag selector will be applied.

So whenever there will be clashing styles in one element, always the styles of the selector with a higher specificity score will be applied to that element.

How Specificity score is calculated?

The specificity score value is a four-column value of four different selector weights. It’s written like INLINE-ID-CLASS-TYPE, each representing four different types of CSS Selectors, with the default scores of 0-0-0-0. As you add more selector components of a specific category, the score of that column increases by 1 each time.

Once the specificity scores have been calculated, they are compared column by column from left to right and the selector with a greater value in the first differing column take precedence!

Let’s understand this using some examples.

Suppose the HTML file looks like this:

<div>
    <button class="button" id="btn"> Learn CSS Specificity </button>
</div>
This HTML file will be the same for all the examples below.
  1. TYPE Column

    Whenever you add an element selector like h1, div, p, etc or pseudo-elements like ::before, ::after, ::placeholder, etc, the score of the TYPE column increases by one (0-0-0-1).

     button {                    /* 0-0-0-1 */
         background-color: blue;
     }
    
     div button {                /* 0-0-0-2 (WINS!) */
         background-color: yellow;
     }
    
  2. CLASS Column

    Whenever you add a class selector like .btn or attribute selectors like [type="email"] or pseudo-classes like :hover, :active, the score of the CLASS column increases by one (0-0-1-0).

     button.btn {                    /* 0-0-1-1 */
         background-color: blue;
     }
    
     button.btn:hover {                /* 0-0-2-1 (WINS!) */
         background-color: yellow;
     }
    

    Do you notice something here? Before understanding specificity, I used to think pseudo-classes like :hover had some built-in priority that automatically overrode styles. But now, it's clear that when :hover is applied, the browser dynamically applies the :hover pseudo-class when the user hovers over the element and its specificity score increases, allowing it to override previous styles.

  3. ID Column

    Whenever you add an ID selector like #submitBtn, the score of the ID column increases by one (0-1-0-0).

     #container button.btn {                    /* 0-1-1-1 */
         background-color: blue;
     }
    
     #container button.btn#submitBtn {                /* 0-2-1-1 (WINS!) */
         background-color: yellow;
     }
    
  4. INLINE Column

    Whenever you add some inline style like style=”background-color: black”, the score the of Inline column increases by one (1-0-0-0) and overrides all the styles which was applied in the stylesheet.

     <div>
         <button class="button" id="btn" style="background-color: black;"> Learn CSS Specificity </button>
     </div>
    

Selectors with matching specificity

In all the above cases, we have discussed how the specificity algorithm works for different specificity score, but what if there are two or more selectors with the same specificity score. For these cases, it follows the basic cascade order rule of CSS, where the styles declared later will override earlier ones if their specificity is equal.

This rule applies not only within the same stylesheet, but also across multiple stylesheets. If two stylesheets define rules with the same specificity, the styles from the stylesheet loaded later in the HTML <head> will take precedence. This is because browsers process stylesheets in the order they are linked.

The !important keyword

By now, we are quite familiar with the Specificity Algorithm of CSS and how it works. But one can bypass this Specificity system and force a style to take precedence over all other selectors—including more specific selectors and even inline styles, by using a special keyword !important beside a style declaration.

Since this keyword comes with so much power, as it alters the default Specificity Algorithm of CSS, and with great power there comes great responsibility and hence it should be used very carefully and sparingly, as it can make debugging difficult. Whenever you are using the !important keyword, make sure to add comment in that line explaining why it is necessary to ensure maintainability and clarity.

Challenge

What will be the background-color of the <button> when hovered over?

<div class="container">
  <button id="btn" class="btn" style="color:white; background-color:black">Button</button>
</div>
.container #btn {
  background-color: blue;
}

.container .btn:hover {
  background-color: red;
}
Solution
The background-color of the <button> when hovered over will stay black. Let’s break it down: The inline style has a score of 1000, the selector .container #btn has a score of 0110 and the .container .btn:hover selector has a score of 0030 and hence the inline style wins and the background color of the button stays black.

Summary

I hope you now have a clear understanding of the CSS Specificity Algorithm. Learning this will definitely help you debug styles that do not apply as intended and will also enable you to write more maintainable and robust styles.