Back to Blog

The CSS Files - Specificity

Andrew Moscardino | May 20th, 2021

Despite some memes, CSS is pretty awesome. It’s simple, powerful, and incredibly flexible. But to some developers, CSS can be confusing. You write a bunch of rules that you think will apply to an HTML element but something isn’t right or some property isn’t being used. Frustrated, you turn to the sledgehammer of CSS tools: !important. It works, but you don’t know why.

To me, seeing !important in CSS is a code-smell. It’s an indication of a larger problem hiding under the surface. If I see !important during a code review, it’s a sure sign the developer needs to re-think their approach.

Often these kinds of issues come down to a misunderstanding of how the browser chooses which CSS rules to apply to an element and what priority it gives to those rules. Let’s dig into how the priority of rules are determined and demystify some of the magic of CSS.


Specificity is a value generated by the browser for each CSS rule. It’s made up of four parts and is based on the types of selectors used and the number of each:

To fill in each spot, you simply count the number of types and fill in the blanks. For instance, consider the following rule:

.foo {
    color: red;

Since only one selector is used and because it is a class selector, the specificity value is “0,0,1,0”. Now let’s look at this one:

#bar div {
    color: red;

That rule uses two types of selectors, element and ID, but only uses one of each resulting in a specificity value of “0,1,0,1”.

How these values are used is simple: the bigger value gets a higher priority. The browser does this with every CSS rule. You can compare values easily by removing the commas and just comparing the numbers. 101 is larger than 10, so the second rule would take priority if they were to both apply to the same element.

The commas are important as the specificity value is not a base-10 value. You can’t beat a single ID selector with more than 10 classes.

Let’s look at some more examples and see if you can figure out how I got the numbers shown:

ul#menu li > a {
    /* 0,1,0,3 */

ul#menu > a {
    /* 0,1,1,3 */

html body.js h1 + p.lead {
    /* 0,0,2,4 */

p.sample > * + * {
    /* 0,0,1,1 */

a[href^="#"] {
    /* 0,0,1,1 */

ul li ~ li div:not(.foo) {
    /* 0,0,1,4 */
<!-- 1,0,0,0 -->
<span style="color: red;"></span>

Some things to consider:

  • The universal selector * adds no specificity.
  • Pseudo-classes (like :hover) count like a class while pseudo-elements (like :first-child) count like an element.
  • :not() is special in that is doesn’t add any specificity, but the clause inside it does. In the above example, the .foo inside the :not() is what adds the 1 in the 3rd spot. This also applies to :is().
  • If a selector is repeated, it will be counted multiple times. So .test.test has a value of “0,0,2,0” while .test is only “0,0,1,0”. I don’t recommend using this in practice.

About Important

!important allows a single property to be moved up such that the specificity of its rule will usually not matter. It can be thought of as adding an extra digit to the specificity calculation resulting in a value of “1,0,0,0,0”. This is one of the only ways to override an inline style.

What about ties?

    span {
        display: inline-block;
        height: 100px;
        width: 100px;

    .red {
        background-color: red;

    .blue {
        background-color: blue;

<span class="blue red"></span>
<span class="red blue"></span>

What color do you think the boxes would be? If you’ve been following along, you shouldn’t be too surprised to find out that they are both blue. See for yourself.


Understanding specificity can take a lot of the mystery out of CSS. Knowing how selectors compare can help you determine what properties will actually be applied. This in turn can help you find issues with missing properties and can prevent you from having to resort to !important.

Learn More


Coyier, Chris. “Specifics on CSS Specificity: CSS-Tricks.” CSS, 20 Jan. 2021,