CSS cascade

On this page 7

The CSS cascade is the algorithm a browser uses to resolve conflicts when several declarations set the same property on the same element, so that exactly one value wins. In CSS, it sorts every competing declaration by origin and importance, context, cascade layers, specificity and finally order of appearance, and the first criterion that separates two declarations decides the outcome.

The cascade is the reason the language is called Cascading Style Sheets. It allows user agent defaults, user preferences and author styles to coexist, and it gives authors predictable tools for overriding styles without rewriting them. Understanding its exact order is the difference between deliberate styling and an escalating war of !important flags.

How the cascade sorts competing declarations

The cascade works on declarations, not rules. For each element and each property, the browser collects every declaration whose selector matches, then compares them through a fixed sequence of criteria. Only when two declarations tie on one criterion does the next criterion apply.

The sequence below reflects the current CSS Cascading and Inheritance specification, Level 5 and later. Earlier mental models that jump straight to specificity skip several steps that now matter in practice, especially cascade layers and scoping proximity.

StepCriterionWhat wins
1Origin and importanceThe higher-ranked origin, with importance reversing the order
2ContextShadow DOM context rules decide between encapsulated styles
3Element-attached stylesDeclarations in the style attribute beat selector-based rules
4Cascade layersLater layers win for normal rules; earlier layers win for important rules
5SpecificityThe selector with the higher (ID, class, type) weight
6Scoping proximityThe @scope root closest to the element
7Order of appearanceThe declaration that appears last in document order
Cascade criteria in the order they are applied

Once a winner exists, the result is the cascaded value. If no declaration matches at all, the property falls back to inheritance for inherited properties such as color and font-family, or to the initial value for non-inherited properties such as margin and border.

Origins, importance and cascade layers

Every declaration belongs to an origin: the user agent stylesheet built into the browser, the user stylesheet configured by the reader, or the author stylesheet written by the site. For normal declarations, author beats user and user beats user agent. Adding !important reverses this order, which protects user accessibility preferences from author styles.

RankOrigin and importanceTypical purpose
1TransitionsInterpolated values during a running transition
2Important user agentBrowser rules that must never be overridden
3Important userForced accessibility choices such as minimum font size
4Important authorDeliberate author overrides
5AnimationsValues produced by running keyframe animations
6Normal authorOrdinary site stylesheets
7Normal userReader preferences without importance
8Normal user agentDefault browser styling of HTML elements
Origin precedence from highest to lowest

Cascade layers, declared with @layer, divide the author origin into ordered buckets. A typical order is reset, base, components, utilities. Any declaration in a later layer beats any declaration in an earlier layer regardless of specificity, and styles outside any layer beat all layered styles. For important declarations the layer order is reversed, mirroring the logic of origins.

Layers solve a long-standing problem: third-party CSS with heavy selectors can be placed in a low layer, so that simple selectors in later layers override it without specificity hacks.

The cascade compared with specificity and inheritance

The cascade is often confused with its components. Specificity is one step inside the cascade, not a synonym for it, and inheritance only operates after the cascade finds no winner. Separating the three concepts clarifies most debugging sessions.

MechanismQuestion it answersWhen it applies
CascadeWhich declaration wins among all candidatesWhenever two or more declarations match
SpecificityWhich selector is more targetedOnly after origin, context and layers tie
InheritanceWhat value a child takes from its parentOnly when no declaration matches the property
CSS variablesWhat value a custom property holdsCustom properties cascade first, then substitute
Media and container queriesWhether a rule applies at allBefore the cascade, by filtering rules
Related mechanisms and how they differ

Custom properties deserve a note: a CSS variable is itself cascaded like any property, and the winning value is then substituted where var() is used. Media queries and container queries do not rank declarations; they decide whether a rule enters the cascade in the first place.

Controlling the cascade in practice

Good architecture uses the cascade deliberately instead of fighting it. The most robust approach in modern CSS combines explicit layer order, low and uniform specificity and the CSS-wide keywords that reset values on purpose.

SituationRecommended toolReason
Third-party library overridesPlace the library in an early @layerYour styles win without heavier selectors
Component needs a local resetall: revert-layer or revertRolls back to the previous layer or origin
Avoiding specificity growth:where() around selectorsContributes zero specificity
Raising one selector slightly:is() with a stronger argumentTakes the highest argument specificity
True one-off utility override!important inside a utilities layerContained and predictable
Practical tools for cascade control

The keywords initial, inherit, unset, revert and revert-layer act on cascade results. `revert` returns the property to the value of the previous origin, usually the browser default, while initial jumps to the specification default, which for display is inline rather than the expected block.

Common cascade mistakes and their causes

Most styling bugs that seem random are deterministic cascade outcomes. Browser developer tools list every matching declaration and strike through the losers, which is the fastest way to see which criterion decided the result.

  • Escalating `!important`: each override needs another one, and the order eventually reverses unexpectedly inside layers.
  • Assuming source order beats specificity: order of appearance applies only after specificity ties.
  • Unlayered styles overriding layers: any rule outside @layer beats every layered rule, which surprises teams that adopt layers gradually.
  • Inline styles winning silently: JavaScript that writes to the style attribute outranks stylesheet selectors.
  • Expecting inheritance on non-inherited properties: padding or border never inherit unless inherit is set explicitly.

A plain-language summary

Imagine several people giving instructions about the same thing, such as the color of a heading. The browser cannot follow all of them, so it uses a fixed ranking system to choose one. First it asks who gave the instruction and how strongly it was marked, then which group it belongs to, then how precisely it points at the element, and finally which came last.

That ranking system is the CSS cascade. Once a designer understands its order, styles stop looking random: every winning value has a clear, traceable reason, and fixing a wrong color becomes a matter of reading the ranking rather than guessing.

An example from practice

A product team integrates a date-picker library whose stylesheet uses selectors such as .dp-wrapper .dp-table td.dp-day. Their own button styles use single classes, so the library wins every conflict, and developers start adding !important to dozens of rules.

The team instead declares @layer reset, vendor, base, components, utilities; at the top of the main stylesheet and imports the library into the vendor layer. Because layer order is checked before specificity, a plain .day rule in the components layer now overrides the library’s four-part selector.

Within one sprint they remove more than forty `!important` flags, and new components no longer need specificity tricks. The outcome is a predictable stylesheet where overrides follow the declared layer order instead of selector length.