← HTML and CSS fundamentals: structure, presentation and diagnosis
05 / 10 · 65 MIN

Cascade, inheritance and selector contracts

Determine which declaration supplies a value and distinguish matching, precedence, inheritance and invalidity after substitution.

Start with matching

In the example, .card > .title requires a direct-child relationship. If a section starts wrapping the title, the rule stops matching even when all classes remain. Increasing specificity or adding !important does not fix a false condition.

Replacing > with a space broadens scope to deeper descendants and may affect nested cards; decide whether that broader scope belongs to the component contract.

An order of decisions

The cascade begins with relevant rules, including selectors and media conditions. Origin, importance and layers precede specificity comparison. Scoped rules can also require a proximity comparison before appearance order.

Exercises bound these factors: author rules, no animations or transitions, and explicitly absent scope. This makes it possible to justify a result without turning a simplification into a universal rule.

Layers as an integration contract

With @layer vendor, app, normal declarations in app take precedence over normal declarations in vendor. The vendor selector’s ID does not change that relationship. However, a normal unlayered rule takes precedence over both.

Reopening vendor after app adds rules without moving the layer. When integrating external CSS, decide where those rules belong and keep the initial order in a clear place; avoid accumulating overrides that conceal the conflict’s cause.

Weight without losing scope

The :where(#catalog) part still requires the #catalog context but adds zero specificity. In :where(#catalog) .card > .title, the two outside classes count. In contrast, :is(.note, #urgent) uses the weight of the most specific argument even on an element matching through .note.

This difference helps design libraries that are easy to adapt and explains why adding an ID alternative can change existing conflicts.

Importance and inline styles

A normal inline style beats normal author stylesheet rules even with ID selectors. That does not mean it beats every cascade mechanism. Among important declarations in author layers, layer order reverses: the first beats the next, and layered important declarations beat unlayered important declarations.

Important user styles and transitions require other criteria; this block’s examples do not simulate them.

Inheritance and resetting

Without an applicable own declaration, color can come from the parent; border and margin are not inherited by default. The unset value follows this distinction: on an inherited property it behaves as inherit; otherwise it uses the initial value.

It does not mean removing a rule and choosing the previous one. During diagnosis, record whether the value comes from a declaration on the element, inheritance or initialization. Similar appearance does not prove that two boxes have the same style source.

Trace var to the computed value

In the example’s label, --tone contains 20px and has no @property registration. That value exists as a custom property but is not a color. The color: var(--tone, red) declaration wins the cascade and only fails after substitution.

The browser does not recover color: blue or choose red because it is a valid color: color ultimately inherits green from the card. Compare this with a missing variable, where the fallback can be used, and correct the value contract before multiplying override rules.

@layer vendor, app;
@layer vendor {
  #catalog .card > .title { color: navy; }
}
@layer app {
  :where(#catalog) .card > .title { color: teal; }
}
.card { color: green; }
.label { color: blue; }
.label { --tone: 20px; color: var(--tone, red); }
IN PRACTICE

Consider a .card inside #catalog with direct children h2.title and span.label. Only these author rules exist, with no inline styles, scope, animations, transitions or important rules. The title becomes teal through layer order.

The label inherits green because 20px is not a valid color after substitution. The red fallback does not act as a type checker.

Common pitfalls

Treating !important as higher specificity; applying normal order to important layers; assuming :where stops constraining matching; using var as type validation; swapping HTML class order to resolve the cascade.

Related topics: Document structure and semantics · Box model, flow and overflow · Diagnosing styles and delivery

Take this idea with you

First identify applicable rules and relevant precedence. Then compare specificity and remaining tie-breakers. If the declaration uses var, trace substitution through computed-value time; inheritance and initial values can explain a result that rule order alone cannot.

Create account

Reference: Cascade order and bounded author-rule comparisons · HTML Living Standard; modular CSS