Skip to content

Dedupe theme stylesheets via content-derived scopes#5440

Merged
burieberry merged 4 commits into
mainfrom
cs-12031-dedupe-theme-stylesheets
Jul 10, 2026
Merged

Dedupe theme stylesheets via content-derived scopes#5440
burieberry merged 4 commits into
mainfrom
cs-12031-dedupe-theme-stylesheets

Conversation

@burieberry

@burieberry burieberry commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Themed cards each emit their own copy of the theme stylesheet so prerendered fragments stay self-contained, but only one copy per theme needs to be active on a page.

  • data-boxel-theme-scope is now derived from the theme card's id plus a 64-bit fingerprint of its CSS, instead of the rendered card's id. Cards sharing a theme emit byte-identical stylesheets; different versions of a theme keep distinct scopes, so cached fragments from before and after a theme edit can't restyle each other. ThemeDashboard derives its scope from the same identity, so both render paths agree.
  • A host ThemeStyleDeduper keeps one copy of each identical theme stylesheet active in the live DOM, promoting a survivor if the active copy is removed. It disables via the disabled property (not an attribute), so serialized prerendered HTML keeps the full stylesheet.

Unit tests pin the scope format as a serialization contract; acceptance tests cover shared-scope stylesheet identity, single-active-copy dedup, and survivor promotion.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Preview deployments

Host Test Results

    1 files  ± 0      1 suites  ±0   2h 53m 45s ⏱️ + 3m 2s
3 461 tests +25  3 446 ✅ +25  15 💤 ±0  0 ❌ ±0 
3 480 runs  +25  3 465 ✅ +25  15 💤 ±0  0 ❌ ±0 

Results for commit 2609046. ± Comparison against earlier commit b69a22b.

Realm Server Test Results

    1 files  ± 0      1 suites  ±0   11m 0s ⏱️ -40s
1 814 tests +22  1 814 ✅ +22  0 💤 ±0  0 ❌ ±0 
1 893 runs  +22  1 893 ✅ +22  0 💤 ±0  0 ❌ ±0 

Results for commit 2609046. ± Comparison against earlier commit b69a22b.

@burieberry
burieberry force-pushed the cs-12031-dedupe-theme-stylesheets branch from 45bc6fb to 7517789 Compare July 9, 2026 17:45
@burieberry burieberry changed the title Scope theme stylesheets by theme id plus content hash Dedupe theme stylesheets via content-derived scopes Jul 9, 2026
@burieberry
burieberry force-pushed the cs-12031-dedupe-theme-stylesheets branch from 7517789 to b69a22b Compare July 9, 2026 22:47
@burieberry
burieberry marked this pull request as ready for review July 9, 2026 22:47
@burieberry
burieberry requested a review from a team July 9, 2026 22:47
@habdelra
habdelra requested a review from Copilot July 9, 2026 22:49

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b69a22b877

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/host/app/lib/theme-style-deduper.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Boxel’s theme scoping so cards that share the same theme produce the same data-boxel-theme-scope (theme card id + CSS fingerprint), enabling host-side deduplication of identical theme <style> blocks while keeping prerendered fragments self-contained and safe across theme edits.

Changes:

  • Introduces themeScope(themeId, css) (theme id + 64-bit FNV-1a fingerprint) and updates theme-scoped rendering paths to use it consistently.
  • Adds a host ThemeStyleDeduper (wired via an instance initializer) to keep only one identical theme stylesheet active in the live DOM by toggling HTMLStyleElement.disabled.
  • Adds unit + acceptance coverage for the scope format contract and for dedup/survivor-promotion behavior.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/host/tests/acceptance/theme-card-test.gts Adds acceptance coverage for shared theme scope identity and live DOM stylesheet dedup/survivor promotion.
packages/host/app/lib/theme-style-deduper.ts New MutationObserver-driven deduper that disables redundant identical theme <style> tags.
packages/host/app/instance-initializers/dedupe-theme-styles.ts Starts/stops the deduper for the app instance lifecycle.
packages/boxel-ui/test-app/tests/unit/theme-scoped-css-test.ts Adds unit tests that pin the themeScope serialization format and behavior.
packages/boxel-ui/addon/src/helpers/theme-scoped-css.ts Implements themeScope() and the 64-bit CSS fingerprint used for stable scoping.
packages/boxel-ui/addon/src/helpers.ts Re-exports themeScope from the addon helpers barrel.
packages/base/style-reference.gts Passes @themeId into ThemeDashboard so its scope matches the container scope.
packages/base/structured-theme.gts Passes @themeId into ThemeDashboard.
packages/base/detailed-style-reference.gts Passes @themeId into ThemeDashboard.
packages/base/brand-guide.gts Passes @themeId into ThemeDashboard.
packages/base/default-templates/theme-dashboard.gts Uses themeScope(themeId, themeCss) (with guid fallback) for persisted-safe scoping and emits scoped theme CSS using that scope.
packages/base/field-component.gts Updates CardContainer theme scope to be theme-derived (theme id + CSS hash) instead of card-id/guid-based.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/host/app/lib/theme-style-deduper.ts Outdated
Comment thread packages/host/app/lib/theme-style-deduper.ts Outdated
burieberry and others added 4 commits July 10, 2026 12:10
Cards sharing a theme previously each emitted their own copy of the
theme stylesheet under a per-card scope, so the CSS payload scaled with
rendered card instances — especially costly for compositions embedding
many themed cards, where every prerendered fragment carries the copies.

Deriving the scope from the theme card's id plus an fnv1a hash of its
CSS makes all cards on one theme emit byte-identical style elements
(harmless as duplicate rules, dedupable by consumers), while the hash
keeps scopes from different versions of a theme distinct so cached
prerendered fragments from before and after a theme edit cannot
restyle each other's cards.

The theme dashboard's inner dark-toggle re-scope now also derives from
content instead of guidFor, which could repeat across prerender jobs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ThemeDashboard previously derived its theme scope from the constant
'theme-dashboard', so distinct themes were separated only by the CSS
hash and the dashboard could never share a scope with the same theme
rendered through field-component. Callers now pass @ThemeID={{@model.id}}
and the guid fallback covers unsaved theme cards previewing their own
CSS, matching field-component's semantics.

The scope's CSS fingerprint grows from one 32-bit FNV-1a pass to two
passes with different seeds, concatenated as fixed-width hex. Scope
equality implies declaration equality is the invariant that keeps
mixed-vintage prerendered fragments from restyling each other; at 32
bits a collision between two versions of a theme was left to chance,
at 64 it is not a practical concern.
Recognize theme stylesheets by marker attribute, not CSS text prefix
@burieberry
burieberry force-pushed the cs-12031-dedupe-theme-stylesheets branch from b69a22b to 2609046 Compare July 10, 2026 16:15
@burieberry
burieberry merged commit 2a2b83a into main Jul 10, 2026
78 checks passed
@burieberry
burieberry deleted the cs-12031-dedupe-theme-stylesheets branch July 10, 2026 21:56
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.

3 participants