Skip to content

Cascade, Specificity, and Inheritance Deep Dive

advanced12 min read

The Algorithm That Decides Every Pixel

You write color: red. The browser ignores it. Why? Because somewhere in an eight-level cascade algorithm, something else won — and it wasn't specificity. Most developers learn "specificity + source order" and call it a day. The reality is that specificity is round 6 of 8. Origins, context, layers, scope, and !important all come first. Once you see the full algorithm, you'll stop guessing why your styles don't apply and start knowing.

Mental Model

Think of the cascade as an eight-round elimination tournament. Two competing declarations enter, and at each round, one criterion is checked. The first criterion to produce a winner ends the match — later rounds are never reached. Most CSS conflicts are resolved by round 3 or 4. But when declarations are closely matched (same origin, same layer, same scope), you reach the later rounds where specificity and source order decide.

The Full Cascade Algorithm (CSS Cascading Level 5)

When multiple declarations target the same property on the same element, the cascade resolves the conflict in this order:

1. Origin and Importance

Declarations are sorted by their origin and whether they're !important:

Priority (highest first)OriginImportance
1Transition declarations
2User agent!important
3User!important
4Author!important
5Animation @keyframes
6AuthorNormal
7UserNormal
8User agentNormal

Notice the inversion: for !important, user agent beats user beats author. For normal declarations, author beats user beats user agent. This protects user accessibility settings.

2. Context (Scoping Root)

In @scope rules, declarations from a narrower scope beat those from a wider scope.

3. Cascade Layers (@layer)

Within the same origin, declarations are sorted by layer order. Later-declared layers have higher priority:

@layer base, components, utilities;
/* utilities > components > base */
/* Unlayered styles beat ALL layers */
Common Trap

Unlayered author styles beat ALL layered author styles, regardless of the layer's position. If you put your library in a layer but your app code is unlayered, your app code always wins — which is exactly the intended behavior.

4. Inline Styles

style="" attributes have higher priority than any non-!important stylesheet declaration within the same origin.

5. Specificity

The (ID, CLASS, ELEMENT) tuple comparison, compared column by column.

6. Scoping Proximity

In @scope rules, declarations from a scope with fewer generational hops to the element have priority.

7. Source Order

The declaration that appears last in source order wins.

8. Default (Initial/Inherited Value)

If no declaration applies, the property uses its inherited value (if inherited) or its initial value.

Inheritance: Which Properties and Why

Not all CSS properties inherit. The distinction matters for understanding default behavior:

Inherited Properties (selected)

/* Typography */
color, font-family, font-size, font-weight, font-style,
letter-spacing, line-height, text-align, text-indent,
text-transform, white-space, word-spacing

/* Lists */
list-style, list-style-type, list-style-position

/* Visibility */
visibility, cursor

/* Other */
direction, writing-mode

Non-Inherited Properties (selected)

/* Box model */
margin, padding, border, width, height

/* Layout */
display, position, float, clear, overflow

/* Visual */
background, box-shadow, opacity, outline

/* Flex/Grid */
flex, grid, align-items, justify-content, gap

Controlling Inheritance

.child {
  color: inherit;     /* Force inheritance (even on non-inherited properties) */
  border: inherit;    /* Inherit border from parent (unusual but valid) */
  padding: initial;   /* Reset to the property's initial value */
  margin: unset;      /* inherited? → inherit. non-inherited? → initial */
  display: revert;    /* Reset to user-agent stylesheet value */
  all: unset;         /* Reset ALL properties to inherit/initial */
}
The difference between initial, unset, and revert

initial always resets to the CSS specification's initial value. For display, that's inline — not block, even on a <div>. unset checks if the property inherits: if yes, it inherits; if no, it uses initial. revert is the most useful — it resets to the user-agent stylesheet value. For a <div>, display: revert gives you block (the browser default), while display: initial gives you inline (the spec initial value).

Cascade Layers and the New Mental Model

Before layers, CSS architecture was a specificity management problem. With @layer, specificity becomes secondary to layer order:

@layer reset, base, components, utilities;

@layer reset {
  /* Highest-specificity selectors here lose to any later layer */
  #app main .hero h1.title { color: black; }
}

@layer utilities {
  /* Lowest specificity wins over reset because it's in a later layer */
  .text-red { color: red; } /* This wins */
}

Layer Organization Strategy

/* Import order defines layer priority */
@layer reset, vendor, base, components, overrides;

@layer reset {
  @import 'reset.css';
}

@layer vendor {
  @import 'some-library.css'; /* Contained — can't escalate */
}

@layer base {
  /* Design tokens, typography, default element styles */
}

@layer components {
  /* Component-specific styles */
}

@layer overrides {
  /* Page-specific overrides — highest layered priority */
}

/* Unlayered styles (highest priority) for truly exceptional cases */
Execution Trace
Origin
Both declarations are author normal
Same origin — move to next criterion
Layer
Declaration A is in @layer base, B is in @layer components
components declared after base — B wins at this step
Specificity
Never reached
Layer order already decided the winner
Key insight
A has specificity (1,3,2), B has (0,1,0)
Doesn't matter — layer order > specificity

Production Scenario: Third-Party CSS Containment

/* Problem: Third-party library uses high-specificity selectors */
/* .ui-dialog .ui-dialog-content .message { color: red !important; } */

/* Solution: Put third-party CSS in a low-priority layer */
@layer vendor, app;

@layer vendor {
  @import 'problematic-library.css';
  /* Even their !important declarations are contained within the vendor layer */
}

@layer app {
  .message { color: blue; } /* Wins over vendor layer */
}
What developers doWhat they should do
Thinking specificity is the main cascade criterion
Specificity is criterion #5 in an 8-step algorithm. Most real-world conflicts are resolved earlier.
Origin, layers, and inline styles are all checked BEFORE specificity
Using display: initial expecting block behavior on a div
initial resets to the spec initial value (inline), not the browser default (block)
Use display: revert to reset to the user-agent default (block for divs)
Assuming all properties inherit by default
If margin inherited, every child would have the parent's margin — which would break all layouts
Only typography, color, and a few other properties inherit. Layout properties don't.
Putting your own styles in a cascade layer and expecting them to beat unlayered code
The spec deliberately gives unlayered styles highest priority within the same origin.
Unlayered styles beat ALL layered styles. Put third-party code in layers, keep your overrides unlayered.
Quiz
Declaration A has specificity (2,5,3) in @layer base. Declaration B has specificity (0,1,0) in @layer utilities. utilities is declared after base. Which wins?
Quiz
What does display: revert do on a <div> element?
Quiz
An unlayered declaration .btn { color: red; } competes with @layer utilities { .btn { color: blue; } }. Which wins?
Key Rules
  1. 1The cascade checks origin, context, layers, inline, specificity, proximity, source order — in that exact sequence
  2. 2Cascade layers are checked BEFORE specificity — layer order beats any specificity difference
  3. 3Unlayered styles beat all layered styles within the same origin
  4. 4Use revert (not initial) to reset to user-agent defaults — initial gives the spec default, not the browser default
  5. 5Only typography, color, visibility, and list-style properties inherit by default — layout properties don't
1/10