The CSS Cascade

Or, How browsers resolve competing CSS styles

We style our websites using CSS, which stands for Cascading Style Sheets.
But what does Cascading really mean?

The CSS Cascade is one of the most powerful parts of CSS. But it can also be very frustrating, if not well understood. Anyone who’s worked on a large enough website has complained "Why won’t this CSS property work?!" And we’ve all been tempted to throw an !important to strong-arm things into place.

To save ourselves from future angst, let’s take a step back and learn this thing for real.

The CSS Cascade is the way our browsers resolve competing CSS declarations.

Every time we write a CSS declaration (or rule), it will enter the CSS Cascade, which will determine whether or not it will end up as the final style. The further down the cascade a declaration falls, the less likely it will end up as the final style.

Let’s take a look at the different tiers of the Cascade.

1.
Importance

The first tier of the Cascade looks at the type of rule we’re looking at.

There are four basic types of rules:

  • 1.
    transition
    Rules that apply to an active transition take the utmost importance
  • 2.
    !important
    When we add !important to the end of our declaration, it jumps to this level of the Cascade. Ideally, you reserve this level for Hail Marys, which are needed to override styles from third-party libraries.
  • 3.
    animation
    Rules that apply to an active animation jump up a level in the Cascade
  • 4.
    normal
    This level is where the bulk of rules live

As you can see, this top tier is mostly reserved to ensure our elements animate properly, and to help out desperate developers (!important).

Let’s look at how this rule plays out:
Which of these two rules would win?

A
p {
color: sandybrown;
}
B
p {
color: orchid !important;
}
vs
Answer
Show me the answer!
Rule B wins!
Remember that !important declarations fall on the second level, while normal declarations fall on the fourth level.

If two rules are tied on one tier of the Cascade, the fight goes on to another round, looking for a difference on the next tier.

2.
Origin

The second tier of the Cascade looks at where the rule was defined.

There are three places where a rule can be defined:

  • 1.
    website
    This is the only level that you have control over, as a web developer.
  • 2.
    user
  • 3.
    browser
    Each browser has its own set of styles, which is why things like <button>s have default styles.

Fight time! Which of these two rules would win?

A
website-stylesheet.css
p {
color: sandybrown;
}
B
Browser default
p {
color: orchid;
}
vs
Answer
Show me the answer!
Rule A wins!
Remember that website-specific declarations fall on the first level, while browser defaults fall on the third level.

3.
Specificity

The third tier of the Cascade looks at the Specificity of a rule.

There are five levels of selectors:

  • 1.
    inline
    Styles declared within a style HTML property are the most specific
  • 2.
    layer
    The new kid in town! Soon, we’ll be able to define explicit “layers” of styles, for intentional handling of specificity. Layers "win" by being defined later, for example:
    style.css
    @layers one, two; // defining our layers
    @layer one {
    body { color: red; }
    }
    @layer two {
    body { color: green; }
    }
    In this example, our body text will be green, since layer two is defined after layer one. Unlayered styles will take precendence, though, to help with backwards compatibility.

    Coming in Chromium 99/Canary, Firefox 97/Nightly, and in Safari Tech Preview. Keep track of the rollout here, or read more in the spec or this wonderful article.


When we create a CSS declaration, we can target specific elements using selectors.

  • 3.
    id
    We can target elements based on their id, using the syntax #id
  • 4.
    class | attribute | pseudo-class
    We can target elements based on their class, using the syntax .class
    This level also includes attribute selectors that target HTML attributes, like [checked] and [href="https://wattenberger.com"]
    This level also includes pseudo-selectors, like :hover and :first-of-type
  • 5.
    type | pseudo-element
    We can target elements based on their tag type, using the syntax type
    This level also includes pseudo-elements, like :before and :selection

Fight time! Which of these two rules would win?

A
*.html
<p style="color: sandybrown">...</p>
B
*.css
p {
color: orchid;
}
vs
Answer
Show me the answer!
Rule A wins!
Remember that inline styles fall on the first level, while type rules fall on the fifth level.


What about these two rules?

A
.paragraph {
color: sandybrown;
}
B
#paragraph {
color: orchid;
}
vs
Answer
Show me the answer!
Rule B wins!
Remember that rules with a class selector fall on the fourth level, while rules with an id selector fall on the third level.


One thing to note about levels on this tier is that the number of hits on the highest-reached level matter.

A
.paragraph:first-of-type {
color: sandybrown;
}
B
p.paragraph {
color: orchid;
}
vs
Answer
Show me the answer!
Rule A wins!
Rule A has two "hits" on the fourth level (1 class and 1 pseudo-class), whereas Rule B has only one "hit" on the fourth level - its "hit" on a lower (fifth) level doesn’t come into play.


Additionally, on this tier of the Cascade, ties can be broken within this tier. This means that, if two rules have the same number of hits on their highest level, one can win by having a hit on the next level down.

A
p#paragraph {
color: sandybrown;
}
B
#paragraph.paragraph {
color: orchid;
}
vs
Answer
Show me the answer!
Rule B wins!
Rules A and B both have 1 hit on the third level (1 id), but Rule B additionally has 1 hit on the fourth level (1 class), which beats Rule A's hit on the fifth level (1 tag).

4.
Position

And lastly, we descend to the fourth, and final, tier of the Cascade, which looks at the order that the rules were defined in.

Rules that are defined later in linked stylesheets or <style> tags will win, given that everything else in the Cascade is the same.

A
p {
color: sandybrown;
color: orchid;
}
B
p {
color: sandybrown;
color: orchid;
}
vs
Answer
Show me the answer!
Rule B wins!

That’s it!

Hopefully, this helps clear up some confusion about styles are prioritized!

Next time you find yourself reaching for the big, red !important button, take a step back and look at where competing styles fall on the Cascading waterfall.

Resources

  • CSS 3 Cascade Spec
    I'd highly recommend a browse through the official spec. It's a great resource, and a fairly easy read, as specs go.