bi-07 — Analysis
Required invariants
- A mutation invalidates a superset of what changed, never a subset. Under-invalidation is a correctness bug (stale rendering); over-invalidation is only a performance bug. The engine is therefore permitted to fall back to whole-subtree invalidation, and never permitted to guess low.
- The stylesheet is compiled into an index before any element is matched.
RuleFeatureSetmaps feature → invalidation sets;RuleSetbuckets rules by key selector. - Selectors match right-to-left, from the key selector upward.
- Identical computed styles are shared. Anything that distinguishes elements defeats it.
- Nothing recomputes at mutation time. Recalculation happens at the next rendering opportunity unless something forces a synchronous flush.
The data that replaces folklore
css_properties.json5 declares invalidate: per property. Measured 2026-08-11: 822 property
entries, 311 declaring invalidation.
| Declaration | Count |
|---|---|
["layout", "paint"] | 95 |
["paint"] | 50 |
["layout"] | 34 |
["layout", "scroll-anchor"] | 13 |
["compositing"] | 4 |
Roughly one property in six triggers layout. The advice "avoid changing CSS in animations" is far too coarse; the table says exactly which.
Note also ["ax-style", ...]: accessibility has its own invalidation category, meaning the
accessibility tree is maintained incrementally as a first-class pipeline consumer, not generated on
demand for a debugger.
Failure modes
| Break | Consequence |
|---|---|
| Omit sibling invalidation | +/~ selectors render stale — the bug the complexity prevents |
| Always fall back to subtree | correct and unusable |
| Defeat style sharing (inline styles on 10k rows) | memory and recalc cliff, not slope |
:has() used casually | inverts matching direction; can widen invalidation dramatically |
getComputedStyle in a write loop | forced synchronous flush per iteration |
The cycle that had to be broken
Container queries make style depend on an ancestor's layout, in a pipeline that runs style → layout. The platform resolves it by requiring containment on the queried axis: a query container's size in that axis must not depend on its contents.
That is the pattern to name: when a declarative system risks circularity, the specification adds a restriction that makes the cycle impossible, rather than an iteration limit that makes it terminate. Restrictions keep a system analysable; limits merely keep it from hanging.