Repository navigation
Use constructable stylesheets for client-created sheets when available - #18
Draft
Pearce-Ropion wants to merge 1 commit into
Draft
Pearce-Ropion wants to merge 1 commit into
Pearce-Ropion wants to merge 1 commit into
Conversation
Avoids CSP style-src conflicts from inserting a <style> element, since constructable stylesheets adopted via adoptedStyleSheets never touch the DOM. Falls back to the existing <style>-element approach (with nonce support) when the browser doesn't support them. Also fixes the hydration scan to include root.adoptedStyleSheets, so reset() rehydrates existing adopted-sheet rule groups instead of duplicating them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What is the purpose of this PR?
Stylesheets created on the client with a
<style>element require CSP'sstyle-src-elemdirective (with'unsafe-inline'or a nonce). This change prefers Constructable Stylesheets (new CSSStyleSheet()+adoptedStyleSheets) instead, which bypass CSP entirely since no DOM element is inserted. It falls back to the original<style>-element + nonce approach when the browser doesn't support constructable stylesheets.What should reviewers focus on?
supportsConstructedStylesheets()detects whetheradoptedStyleSheetsexist,CSSStyleSheetcan be called, andreplaceSyncis available. Confirm this detection guards both the adoption path and the fallback correctly.details
The feature-detection check at packages/core/src/sheet.js#L27-L35 protects against calling
new CSSStyleSheet()or accessingadoptedStyleSheetson an unsupported target (e.g. an older browser or a Node/test environment without polyfills). It uses a try-catch because feature-detection across different runtime shapes is fragile.The creation path at packages/core/src/sheet.js#L150-L160 only branches into adopted stylesheet construction if the feature-detection passes; otherwise it falls through to the original
<style>-element code.The hydration scan at packages/core/src/sheet.js#L83-L84 now includes both
root.styleSheets(from<style>and<link>elements) androot.adoptedStyleSheets(from prior constructable stylesheet adoption). Without this, callingreset()a second time on an adopted-stylesheet-based sheet would fail to rehydrate and duplicate marker rules on every reset. Confirm this merges the arrays in the right order and handles missing properties gracefully.details
When a sheet is first created with adopted stylesheets, the hydration scan saves the internal rule groups by finding the marker rules. On
reset(), the scan runs again to rehydrate — but ifreset()had only scannedroot.styleSheets(the old behavior), it would see zero stylesheets, fail to find the markers, and create new duplicates.The fix adds
root.adoptedStyleSheetsto the scanned list so the rehydration finds the same groups again. The order matters: scanned first are the regular stylesheets (which come earlier logically), then adopted stylesheets. The fallback|| []on each property guards against runtimes where either property doesn't exist.How is this PR tested?
A new test suite in packages/core/tests/sheet-adopted-stylesheets.js covers adopted stylesheet adoption, rule serialization, and rehydration across
reset()calls, plus the fallback to<style>elements when constructable stylesheets are unsupported.details
The test suite uses a minimal fake CSSOM/DOM in packages/core/tests/internal/fakeDom.js (jsdom doesn't implement constructable stylesheets, and a real browser wasn't available).
installFakeGlobals()mocksCSSStyleSheet,document, andwindowas ambient globals, then returns a restore function that clears them. The test runner executes every test file sequentially in one shared Node process, so leaking these globals would break unrelated packages — the restore call at the end of this file prevents that.Key coverage:
createSheet()adopts a stylesheet when supported (verified by checkingadoptedStyleSheetslength and membership). A secondreset()call must not adopt a second stylesheet or duplicate marker/media-group pairs (the count of marker rules must matchnames.length). Rules applied afterreset()must append to the rehydrated group, not a duplicate one. The fallback path must create a<style>element whenreplaceSyncis absent from the prototype.🏗️ Generated for Sigma by Claude Code