diff --git a/packages/blockly/core/css.ts b/packages/blockly/core/css.ts index 46abe465c..6e935a7ec 100644 --- a/packages/blockly/core/css.ts +++ b/packages/blockly/core/css.ts @@ -5,9 +5,8 @@ */ // Former goog.module ID: Blockly.Css -/** Has CSS already been injected? */ const injectionSites = new WeakSet(); -const registeredStyleSheets: Array = []; +const registeredCss: Array = []; import * as userAgent from './utils/useragent.js'; /** @@ -17,11 +16,7 @@ import * as userAgent from './utils/useragent.js'; * @param cssContent Multiline CSS string or an array of single lines of CSS. */ export function register(cssContent: string) { - if (typeof window === 'undefined' || !window.CSSStyleSheet) return; - - const sheet = new CSSStyleSheet(); - sheet.replace(cssContent); - registeredStyleSheets.push(sheet); + registeredCss.push(cssContent); } /** @@ -41,24 +36,26 @@ export function inject( hasCss: boolean, pathToMedia: string, ) { - if (!hasCss || typeof window === 'undefined' || !window.CSSStyleSheet) { - return; - } + if (!hasCss || typeof window === 'undefined') return; const root = container.getRootNode() as Document | ShadowRoot; - // Only inject the CSS once. if (injectionSites.has(root)) return; injectionSites.add(root); // Strip off any trailing slash (either Unix or Windows). const mediaPath = pathToMedia.replace(/[\\/]$/, ''); - const cssContent = content.replace(/<<>>/g, mediaPath); + const cssText = [content, ...registeredCss] + .join('\n') + .replace(/<<>>/g, mediaPath); - const sheet = new CSSStyleSheet(); - sheet.replace(cssContent); - root.adoptedStyleSheets.push(sheet); - - registeredStyleSheets.forEach((sheet) => root.adoptedStyleSheets.push(sheet)); + const styleEl = document.createElement('style'); + styleEl.id = 'blockly-common-style'; + styleEl.textContent = cssText; + // Prepend so Blockly's rules sit at the start of the cascade; any user + // stylesheet declared later wins by document order. Style elements appended + // to the light DOM don't apply inside shadow roots, so for the shadow DOM + // case we prepend the style element to the shadow root itself. + (root instanceof ShadowRoot ? root : document.head).prepend(styleEl); } /** diff --git a/packages/blockly/core/renderers/common/constants.ts b/packages/blockly/core/renderers/common/constants.ts index 8efe0ae33..20a7f7b1a 100644 --- a/packages/blockly/core/renderers/common/constants.ts +++ b/packages/blockly/core/renderers/common/constants.ts @@ -1126,17 +1126,15 @@ export class ConstantProvider { * @param selector The CSS selector to interpolate into the stylesheet. */ protected injectCSS_(root: Document | ShadowRoot, selector: string) { - if ( - typeof window === 'undefined' || - !window.CSSStyleSheet || - injectionSites.get(selector)?.has(root) - ) { - return; - } + if (typeof window === 'undefined') return; + if (injectionSites.get(selector)?.has(root)) return; - const sheet = new CSSStyleSheet(); - sheet.replace(this.getCSS_(selector).join('\n')); - root.adoptedStyleSheets.push(sheet); + const styleEl = document.createElement('style'); + styleEl.className = 'blockly-renderer-style'; + styleEl.textContent = this.getCSS_(selector).join('\n'); + // See css.ts inject() for the rationale on prepending and shadow root + // handling. + (root instanceof ShadowRoot ? root : document.head).prepend(styleEl); const sitesForSelector = injectionSites.get(selector) ?? new WeakSet();