How to calculate CSS specificity of your style rules
Tags: css, webdev, beginners, react
Published: 2023-06-06 13:16:15

In this article, you will learn how to calculate CSS specificity of the styles you write by computing a compound number to measure specificity weight by CSS selector.
<!-- Selector found to have the greatest weight overcomes style declarations by other selectors to actually apply its style declarations to that element. -->

First off,

**Specificity** is the algorithm browsers use to establish the CSS _declarations_ that will get applied to an element, when it is referenced by two or more _style rules_.<br/>
Specificity algorithm calculates **weight** of CSS selector, and among competing selectors targetting one element, the selector with the greatest **weight** will win to apply CSS declarations to that element.

## Visual definition of CSS terms

<!-- ![CSS syntax by name](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1nqgu2zz37373fv64fpu.jpg) -->

![CSS syntax definitions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l3x75xjxvo0whrx5mio7.jpg)

## The 3-column value

The specificity algorithm bases weight calculation around a **three-column compound number**. Each column represents weight that correspond to the three types of CSS selectors, i.e **ID**, **CLASS** and **TYPE**. This _three-column compound number_ starts off with zeroes looking like:

![Specificity 3-column value initial](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9e3sbft0tein8avvue1d.png)

-   **ID column** - Includes **only** _[ID selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/ID_selectors)_, such as `#app`. For each ID in a matching selector, add **`[1, 0, 0]`** to the weight value.
-   **CLASS column** - Includes _[class selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/Class_selectors)_, such as `.myInput`, _attribute selectors_ e.g `[type="password"]`, and _pseudo-classes_ such as `:hover`, `:first-of-type`, e.t.c. For each class, attribute selector, or pseudo-class in a matching selector, add **`[0, 1, 0]`** to the weight value.
-   **TYPE column** - Includes _[type selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/Type_selectors)_, such as `p`, `h2`, and `a`, and _pseudo-elements_ like `::before`, `::placeholder`, and all other selectors with double-colon(`::`) notation. For each type or pseudo-element in a matching selector, add **`[0, 0, 1]`** to the weight value.

## Specificity comparison

Let's look at the following rule, which is composed of 7 selectors to target a `span` element:

```css
#app #navigation #list .sub-list .item p span {
	/* declarations here */
}
```

There are **3 ID selectors**(`#app`, `#navigation`, `#list`). So the _three-column value_ becomes **`[3, 0, 0]`**.<br />
Then **2 class selectors**(`.sub-list`, `.item`). The _three-column value_ compounds to **`[3, 2, 0]`**.<br />
Then **2 type selectors**(`p`, `span`). The _three-column value_ compounds to **`[3, 2, 2]`**.<br />

Now provided the number in each column is **`9` or less**, we can concatenate the numbers in each column to get a base10 number as the resultant specificity weight. So in this case we have a calculated specificity weight as `322`.

> **Disclaimer:** This is not how specificity is calculated by CSS processor inside browsers, but only a blue print. Normally in browsers, the specificity is not calculated in base 10 number system, but in a larger number, often unspecified.

Suppose we have another rule styling the same `span` element looking like this:

```css
#navigation #list .sub-list .item .wrapper p span {
	/* declarations here */
}
```

Likewise in the style rule above, 7 selectors are used to target the `span` element. Difference being that we have **2 ID selectors** and **3 class selectors**, which results in the _three-column value_ **`[2, 3, 2]`**. Hence a specificity weight `232`. Because `322` is greater than `232`, the former has precedence over the latter. Therefore, the former style rule will be processed to style the `span` element.

## Selectors that add no weight to specificity calculation

The universal selector `*` and the pseudo-class `:where()` with its parameters **do not add onto specificity weight** so their weight is **`[0, 0, 0]`**; _but they do match elements_.

Combinators, such as [`+`](https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator, "Adjascent sibling combinator"),[`>`](https://developer.mozilla.org/en-US/docs/Web/CSS/Child_combinator, "Child combinator"), [`~`](https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_combinator, "Sibling combinator"), [`" "`](https://developer.mozilla.org/en-US/docs/Web/CSS/Descendant_combinator, "Descendant combinator"), and [`||`](https://developer.mozilla.org/en-US/docs/Web/CSS/Column_combinator, "Column combinator"), may make a selector more specific in what is selected but they **don't** add any weight to specificity.

`:not()`, `:has()` and `:is()` pseudo classes add no weight by themselves. However their parameters **do impact weight in specificity calculation**. Therefore, the weight calculated from the selectors, is inclusive of the weight from the parameters of these pseudo-classes.<br>
Incase of a parameter list, weight by the parameters with the highest specificity is used. For example:

```css
/* 
<span id="intro" class="lead">...</span> 
*/

span:is(.lead, #intro) {
	/* calculated specificity - [1, 0, 1] */
}
```

In the above snippet, inside the `:is()` parameter list, `#intro` has higher weight (`[1, 0, 0]`) than `.lead` (`[0, 1, 0]`) . Hence weight by `#intro` is used to sum up with weight by the `span` selector resulting to `[1, 0, 1]`.

## Example

Consider the following <span id="sample-html">HTML</span>:

```html
<html>
	<head>
		...
	</head>
	<body>
		<main id="myElement">
			<input type="password" required class="myInput" />
		</main>
	</body>
</html>
```

In the below CSS, three rules are targeting `<input />` element to set a color. For a given input, `color` value is applied should its selectors specificity weight have precedence over the other matching selectors.

```css
/* FIRST RULE */
#myElement input.myInput {
	color: red;
} /* [1, 1, 1] */

/* SECOND RULE */
input[type="password"]:required {
	color: blue;
} /* [0, 2, 1] */

/* THIRD RULE */
html body main input {
	color: green;
} /* [0, 0, 4] */
```

All the above rules have selectors matching the same and only `<input />` element on our HTML. The input will have a color `red` because the _first rule has the highest specificity weight_, i.e `111` is the greatest number from the weights of `111`, `021` and `004`.

The last rule has **4 type selctors**(`html`, `body`, `main`, `input`). Increasing even more type selctors will still result in `<input />` being color `red` since specificity weight by type selector is always lower than ID selector.

## Excercise

Consider the [same html](#sample-html) to deduce the result of the below CSS:

```css
.myInput {
	border: 0;
}

:is(input.myInput) {
	border: 1px solid black;
}

:where(#myElement input[type="password"]) {
	border: 4px solid blue;
}
```

_What will happen to border on the `<input />` element?_<br />
Share your answer in the comments👍️.

## A Column number that is greater than `9`

I had mentioned earlier that to get the specificity weight you concatenate the column values to get a number like `322`. This is because for less than 10 selectors of a type(_either ID, Class or Type_), we can calculate the specificity in the base 10. Hence concatenating, we can get a number in the base10 number system.

This is not the case when we have 10 and above selectors of a type. High number of selectors can occur when using nesting feature of css preprocessors like [sass](https://sass-lang.com) to generate css. You can end up with _three-column value_ like `[11, 7, 19]`. You can't concatenate the numbers to obtain a **decimal specificity weight** in this case.

But instead you can convert the column numbers to a higher base.<br />
For example, lets say we have a three-column value of `[11, 7, 19]`. To calculate the weight, we can pick a base number that is **larger than the largest column number** in the _three-column value_.<br />
Let's pick a base number `20` which is bigger than `19`(largest number in `[11, 7, 19]`). Then multiply out the three columns starting rightmost working to the left. And add the results, like shown:

```
(20) * 19       =     380
(20*20) * 7     =   2 800
(20*20*20) * 11 =  88 000
-------------------------
Total in decimal = 91 180

```

Doing this will also require you to use that base number to calculate specificity weight for the other competing selectors. Thankfully, CSS processor handles all this for you. so you just have to understand "why a certain style has been applied and not the other".

## Equivalent weights

Where 2 or more style rules have exactly the same specificity weight, the most recent rule will take precedence. However you can force a rule to a higher precedence over other equivalent rules using `!important` keyword like:

```css
p {
	color: #00ff00 !important;
}
```

When you do this, all previous style rules with equivalent weight are overridden(including ones using `!important`) and any equivalent rules that are processed will be ignored.

For example:

```css
p {
	color: #00ff00 !important;
} /* [0, 0, 1] */
p {
	color: #ff0000;
} /* [0, 0, 1] */
```

Both rules have the same weight but the first rule will take precedence because of use of `!important` keyword. Otherwise, the second rule would have taken precendence.

## Conclusion

CSS has a simple syntax with alot happening behind scenes with the CSS processor. We have talked about specificity in this article, but specificity only comes to play after browser has determined [cascade origin and importance](https://developer.mozilla.org/en-US/docs/Web/CSS/Cascade). You can explore more which is beyond scope of this article.

In a nutshell, knowing specificity of your styles helps you construct rules and understand what precedence they will have.<br />
We have not talked about _inline styles_, which are the **most specific**, meaning that when an inline style is applied, it has the highest specificty overriding any style in the style sheet. The only way to override an inline style from a style sheet is to use `!important` keyword.

---

<b>Meet me aside on</b>

-   [Twitter](https://twitter.com/intent/user?screen_name=smitterhane)
-   [Linkedin](https://www.linkedin.com/in/zacky-aduoli)
-   [Github](https://github.com/hane-smitter)

I periodically share useful content you may not want to miss.