demo · v130

source-order showdown

Three real-world scenarios where the order of a bare declaration relative to a nested rule changes the winner. Each panel renders the same CSS twice — once with the legacy pre-spec behaviour (bare declarations hoisted above all nested rules) and once with the Chrome 130 behaviour (bare declarations wrapped in CSSNestedDeclarations at their source position). The pair that disagrees tells you a bug that was unfixable without this change.

pre-spec behaviour (legacy)


        

Chrome 130 (source-order)


        

why source order matters

CSS cascade rules say: when two declarations have the same selector and the same specificity, the one declared later wins. Pre-spec nesting violated this for declarations that came after a nested rule — they were silently re-ordered above the nested rule by the parser. That meant the cascade was no longer source-ordered for any rule using nesting.

Chrome 130 fixes this by wrapping bare declarations after a nested rule in a CSSNestedDeclarations rule, leaving them at their source position. Source-order semantics are restored; CSS authors get the cascade they expect.

the code

/* Pre-spec parsing:
   .card {
     background: white;     // bare decls hoisted
     background: gold;      // hoisted
     & .icon { ... }
   }
   final background: gold (because both are above the nested rule and last-wins) */

/* Chrome 130 parsing:
   .card {
     background: white;
     & .icon { ... }                 // CSSStyleRule
     CSSNestedDeclarations {          // implicit wrapper
       background: gold;
     }
   }
   final background: gold (source order honoured) */

see also