CSS Cascade: Specificity, Inheritance and Style Resolution
How Blink turns a pile of stylesheets into one computed style per element. Specificity is a packed 24-bit integer whose fields saturate rather than carry, !important inverts the origin order instead of adding to it, and a stylesheet that arrives after <body> stops blocking paint while still blocking scripts. Every behaviour here is read from Chromium 154 source, not from the spec's prose.
What you'll explore
- 01
Specificity
Blink does not weigh selectors, it packs them. Each simple selector adds into one of three 8-bit fields of a single unsigned integer, and each field clamps at its maximum instead of carrying into the one above it. That is why a selector's specificity can be compared with a single integer comparison, and why no quantity of classes can ever reach the ID field.
- 02
Inheritance
An inherited property is not copied by a per-property loop at resolve time. The resolver seeds an element's style by constructing a builder from the parent's computed style, so inherited values are already in place before any declaration is applied. A separate fast path exists for properties that can be updated on their own without a full re-resolve.
- 03
Cascade origin and !important
Origin is a small bitfield: user-agent, user, author, animation, transition. For !important declarations the low four bits are inverted, which reverses the ordering rather than shifting it. The consequence surprises most engineers: a user-agent !important outranks an author !important, which is what makes accessibility and print defaults unoverridable by page CSS.
- 04
Computed values
Resolving style runs the whole cascade for one element and produces a ComputedStyle. Reading it back through getComputedStyle() is a different operation again: for layout-dependent properties the CSSOM returns the resolved value, which may require layout to have run, rather than the value the cascade computed.
- 05
Render-blocking CSS
A stylesheet blocks first paint only while no <body> element exists yet. Once the parser has opened <body>, a newly discovered sheet is no longer render-blocking — but it is still counted as script-blocking and parser-blocking, so it delays script execution instead of paint. A late stylesheet is not free; it trades a blank screen for a stall somewhere else.
Validated against Chromium Blink — core/css style resolution chromium-154.0.8037.57 — third_party/blink/renderer/core/css/css_selector.cc and friends. Every simulated behavior cites a real source function, and a server-side correlation engine computes how each layer's state cascades into the next. See the methodology →
This module is part of zerohop Pro — it unlocks alongside the full kata library, progress tracking, and weak-area analysis.