Skip to content

Scope page author stylesheet variants to routes in place - #3548

Merged
chubes4 merged 10 commits into
trunkfrom
fix/2755-route-scoped-variants
Oct 27, 2026
Merged

chubes4 merged 10 commits into
trunkfrom
fix/2755-route-scoped-variants

Conversation

@chubes4

@chubes4 chubes4 commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2755.

Problem

When pages project a shared author stylesheet differently, the compiler writes a full .page-<hash>.css copy for every page that differs. On a 112-route Bootstrap 5 site this produced 489 page copies (75.7 MB), and every page downloads its own uncached copy.

Design

Page variants are reconciled into the shared stylesheet in place, at their source cascade position, and scoped to the routes that keep them:

  • Every plan route's <html> carries be-r plus a short collision-free be-r-<8 hex> route class (DocumentRootContext).
  • Rules are expanded into selector arms. Each arm gets the set of routes that keep it.
  • An arm stays unscoped when every route outside its set provably can't match it. The proof uses page-local engine-class carriers (cheap), then RouteSelectorMatchIndex (key prefilter + CssSelectorMatcher) on compiled markup. Undecidable selectors stay scoped.
  • Render-time route state (aria-current, current-menu/current_page classes, BE route-state classes) is never proven inert. WordPress sets it at render time, so compiled markup can't disprove it.
  • Non-plan pages (WooCommerce, search, archives, 404) are modelled as a virtual route that receives the unprojected shared sheet. Arms it keeps use :where(:root:not(<outside routes>)). Variant-only arms exclude it with the shorter of :where(:root:is(<routes>)) or :where(:root.be-r:not(<outside routes>)). Its inertness proof uses only the engine classes carried by shared template-part markup.
  • Top-level @import rules that follow other rules, which CSS ignores, are dropped before comparison. Differing leading imports still fall back.
  • Budget guard: if the recompacted shared sheet grows by more than max(3× largest page copy, total copy bytes / 10), that sheet falls back to page copies with an author_stylesheet_route_scope_fallback diagnostic. So this change never ships more than trunk would.

Real-site verification

Full 112-route import (liguetirdauphinesavoie.com capture) through Studio site create --from with SSI vendoring this branch, against the same import on trunk:

trunk this PR
.page- stylesheet copies 489 204
Theme size 162 MB 142 MB
Compose 449 s 461 s
Quality gate (fallback / core/html blocks) 0 / 0 0 / 0

Visual parity: all 110 published URLs (pages, posts, WooCommerce shop/cart/checkout/product/my-account) at desktop 1440 and mobile 390, compared by screenshot and by every element's computed style and geometry. 216 of 220 page-views are identical. The 4 that differ are the product page, whose only difference is the local port in a WooCommerce background URL, and 1 px text-metric shifts in the home page footer.

The visual check caught two regressions in earlier iterations, both fixed here with contract tests: render-time aria-current treated as inert (wrong active dropdown item on 31 routes), and non-plan pages losing base rules (unstyled WooCommerce pages).

bootstrap (+5.2 MB vs 4.3 MB limit) and common (+2.3 MB vs 1.9 MB) still fall back on this site, so further scope reduction is possible as a follow-up.

Tests

tests/contract/shared-author-stylesheet-scaling.php covers route scoping, the inertness proofs, the render-time state and non-plan cases, ignored imports, the short exclusion scope and the shipped-byte budget. Each case fails without its fix. Also: tests/unit/css-stylesheet-transformer.php, tests/unit/document-root-font-ownership.php, composer test:canonical, composer lint.

AI assistance

Implemented by GPT-6.1 Sol and Claude Opus via Homeboy Cook (Pi via Roadie). Claude Opus did the real-site measurement, instrumentation, visual parity check, the @import follow-up and review. Chris approved each direction.

@chubes4
chubes4 force-pushed the fix/2755-route-scoped-variants branch from 61cbf72 to 37cba59 Compare October 26, 2026 23:42
@chubes4
chubes4 force-pushed the fix/2755-route-scoped-variants branch from 6fa3e21 to ff5af83 Compare October 27, 2026 02:39
@chubes4
chubes4 marked this pull request as ready for review October 27, 2026 03:31
@chubes4
chubes4 merged commit 41f723e into trunk Oct 27, 2026
15 checks passed
@chubes4
chubes4 deleted the fix/2755-route-scoped-variants branch October 27, 2026 17:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generated theme CSS grows with pages × elements: rewritten selectors enumerate per-page element classes

1 participant