CodeTips#2: How Specificity works in CSS

When working on web apps, at some point, you will need to overwrite styles from components to which you have no access to the CSS source code, or maybe you are setting a new style for an element and it is not working. Often the approach we’d feel compelled to is simply using !important because it is the easier path. However, I strongly believe that in most cases where !important is used it wasn’t even needed if you know how CSS works.

CSS means Cascading Style Sheets. By the name only we can infer that the style of the elements will, in a way, inherit from styles defined beforehand. See the example below:

<div></div>
<div class="small"></div>
<div class="small red"></div>
div {
  background: blue;
  height: 100px;
  width: 100px;
  display: inline-block;
}

.small{
  height: 50px;
  width: 50px;
}

.red {
  background: red;
}

In this example, we have set all the divs as 100x100px blocks, and then created two classes:

  • small: to only change the size of the block to 50x50px
  • red: to change the background to red

Notice that setting a different style in each class will overwrite the size and background color when using the classes small and red respectively.

Now look this example:

div {
  background: blue;
  height: 100px;
  width: 100px;
}

#box {
  background: teal;
}

.box{
  background: red;
}

// HTML
<div id="box" class="box"></div>

What background color will the div have?

You might think it will be red because the class .box set red right below the id selector, but in this case, the background color will be teal.

When we create different selectors and set different values for the same property, like in the previous example for div, #box, and .box with the background color for each one, CSS will have to decide which of those are to be applied. To do that CSS applies a concept called specificity.

Specificity will give a score for each selector and, the one with the biggest will be selected to style the element. The table below is ordered by priority. The more on the left side, the more relevant.

!important style attributes id Class, Attribute selector, pseudo-classes Type, pseudo-elements
!important #id .class, [type=”input”], :hover h1, ::before

Let’s see the example again and calculate the selector score.

div {
  background: blue;
  height: 100px;
  width: 100px;
}

#box {
  background: teal;
}

.box-1{
  background: red;
}

// HTML
<div id="box" class="box-1"></div>

The table of points will be:

selector !important style attributes id Class, Attribute selector, pseudo-classes Type, pseudo-elements
div 0 0 0 0 1
#box 0 0 1 0 0
.box-1 0 0 0 1 0

The biggest value we got is #box, which is 00100.

NOTE: Those values are not in base 10. Even if the column Class has 20 points and the id one has 1, the id will be the winner.

seletor !important style attributes id Class, Attribute selector, pseudo-classes Type, pseudo-elements
#box 0 0 1 0 0
.class 20times 0 0 0 20 0

What happens if we have a draw? The cascade rule will take place and, the selector defined later in the sheet will be picked.

Some cases that doesn’t impact the specificity calculation:

  • Universal selector(*)
  • Combinators(+, >, ~, ‘’, ||)
  • Negation pseudo-class (:not())

Let’s calculate some selectors to tie this concept better:

div#box {background: blue;} /* 0, 0, 1, 0, 1 */ <-- Winner
#box {background: teal;}    /* 0, 0, 1, 0, 0 */
.box-1{ background: red;}   /* 0, 0, 0, 1, 0 */

table td {border: 3px solid black !important}   /* 1,0,0,0,2 */
#table td {border: 3px solid purple !important} /* 1,0,1,0,1 */ <-- Winner
.table td {border: 3px solid green !important}  /* 1,0,0,1,1 */

And that’s it. Remember: before using !important, it’s important to know how specificity works and try to work with different selectors.

We are hiring new talents. Do you want to work with us? become@codeminer42.com