Skip to content
AgileLogics
7 min read

The Cascade in Depth

The full cascade sorts competing declarations by these criteria, in order. The first one that differs decides:

  1. Origin and importance
  2. Context (shadow DOM: outer tree vs inner tree)
  3. Element-attached styles (style="...")
  4. Cascade layers
  5. Specificity
  6. Scope proximity (@scope)
  7. Order of appearance

Origins

There are three main origins:

  • User-agent — the browser's default styles (why h1 is big and links are blue).
  • User — styles the visitor sets (some browsers and extensions allow a custom stylesheet, e.g. larger fonts or high contrast).
  • Author — your stylesheets.

For normal declarations the order of precedence is user-agent < user < author. For !important declarations it reverses: author important < user important < user-agent important. That's deliberate: a user's !important accessibility styles beat anything a site does.

Animations and transitions also have their own places in this order: running animations override normal declarations, and transitions override even important ones while they run.

The global keywords, precisely

<style>
  .parent { color: rgb(200, 0, 0); }
  .a { color: inherit; }
  .b { color: initial; }
  .c { color: unset; }
  .d { color: revert; }
  .e { display: revert; }
  .f { display: initial; }
</style>
<div class="parent">
  <p class="a">a</p><p class="b">b</p><p class="c">c</p><p class="d">d</p><p class="e">e</p><p class="f">f</p>
</div>
<script>
  for (const p of document.querySelectorAll(".parent p")) {
    const s = getComputedStyle(p);
    console.log(p.className, s.color, s.display);
  }
</script>
a rgb(200, 0, 0) block
b rgb(0, 0, 0) block
c rgb(200, 0, 0) block
d rgb(200, 0, 0) block
e rgb(200, 0, 0) block
f rgb(200, 0, 0) inline
  • inherit took the parent's red; unset did the same, because color is an inherited property.
  • initial gave color's initial value (canvastext, black here).
  • revert rolled back to the previous origin — for color there is no browser rule for p, so it inherited red; for display the browser's p { display: block } came back.
  • display: initial is inline — the spec's initial value, not the browser default for p. This is why initial is rarely what you want.
  • revert-layer rolls back to the previous cascade layer (next lesson).

Why this matters in practice

  • all: revert on an element removes every author style — handy inside components that must not inherit site styles.
  • Inline style attributes beat any selector (step 3), but not !important declarations in a stylesheet (step 1).
  • Specificity doesn't matter across layers — step 4 comes first. That's the key to the next lesson.