Skip to content

feat(global-styles): add global styles support to shadow DOM components#6268

Merged
christian-bromann merged 9 commits intomainfrom
cb/global-style-support-in-shadow-dom
May 30, 2025
Merged

feat(global-styles): add global styles support to shadow DOM components#6268
christian-bromann merged 9 commits intomainfrom
cb/global-style-support-in-shadow-dom

Conversation

@christian-bromann
Copy link
Copy Markdown
Member

What is the current behavior?

Currently, Stencil has duplicate shadow root creation logic scattered across multiple runtime files (bootstrap-lazy.ts, bootstrap-custom-element.ts, and proxy-host-element.ts). Each location manually calls attachShadow() with conditional logic for delegatesFocus support.

Additionally, there is no mechanism to apply global styles to shadow DOM components. While Stencil supports global styles for non-shadow components, shadow DOM components remain isolated from these styles, creating inconsistencies in styling across component types.

The existing global styles infrastructure exists but is only used for generating output files - it's not integrated into the runtime for shadow DOM components.

fixes #1967

What is the new behavior?

This PR introduces comprehensive global styles support for shadow DOM components by:

🎨 Global Styles Integration

  • Automatic Application: Global styles are now automatically applied to all shadow DOM components via adoptedStyleSheets
  • Performance Optimized: Uses constructable stylesheets for efficient style sharing across shadow roots
  • Build-time Integration: Global styles are compiled and bundled at build time, eliminating runtime style processing

🔧 Code Consolidation

  • Unified Shadow Root Creation: Created a new createShadowRoot() utility function in src/utils/shadow-root.ts
  • DRY Principle: Eliminated duplicate shadow root creation code across three runtime files
  • Consistent Behavior: Ensures all shadow roots are created with the same logic and global styles applied

🏗️ Build System Enhancements

  • @app-globals Module: Extended the module system to export globalStyles alongside existing globalScripts
  • Cross-Platform Support: Updated esbuild configurations for client, hydrate, and testing platforms to handle the new module
  • Type Safety: Updated TypeScript declarations to reflect that BuildCtx.stylesPromise now returns the compiled styles string

📦 Bundle Generation

  • App Data Plugin: Enhanced to include global styles in the generated bundles
  • Conditional Building: Global styles are only included when config.globalStyle is defined
  • JSON Serialization: Styles are properly serialized and embedded in the bundle for runtime access

Technical Implementation Details

New Utility Function

export function createShadowRoot(this: HTMLElement, cmpMeta: d.ComponentRuntimeMeta) {
  const shadowRoot = BUILD.shadowDelegatesFocus
    ? this.attachShadow({
        mode: 'open',
        delegatesFocus: !!(cmpMeta.$flags$ & CMP_FLAGS.shadowDelegatesFocus),
      })
    : this.attachShadow({ mode: 'open' });

  const sheet = new CSSStyleSheet();
  sheet.replaceSync(globalStyles);
  shadowRoot.adoptedStyleSheets.push(sheet);
}

Build Process Integration

  • Global styles are processed during the style build phase
  • The compiled CSS is made available through the @app-globals module
  • Build configurations across all platforms now include the necessary external alias for @app-globals

Does this introduce a breaking change?

  • Yes
  • No

This is a purely additive feature that enhances existing functionality without breaking current behavior:

  • Backward Compatible: Existing shadow DOM components continue to work exactly as before
  • Automatic Enhancement: Global styles are automatically applied without requiring code changes
  • Performance Improvement: The code consolidation actually improves performance by reducing bundle size
  • Optional Feature: Global styles are only applied when globalStyle is configured in the Stencil config

- Create new createShadowRoot utility function that applies global styles via adoptedStyleSheets
- Update build system to handle @app-globals imports across all platforms (client, hydrate, testing)
- Extend BuildCtx.stylesPromise to return compiled global styles string instead of void
- Replace duplicate shadow root creation code with shared utility across runtime files
- Add globalStyles export to app-globals module for CSS injection into shadow roots
- Update app-data-plugin to include global styles in the bundle generation process

This enables global styles to be automatically applied to all shadow DOM components
while maintaining encapsulation and performance benefits of constructable stylesheets.
@christian-bromann christian-bromann requested a review from a team as a code owner May 29, 2025 22:45
@christian-bromann christian-bromann merged commit 33363d4 into main May 30, 2025
141 of 144 checks passed
@christian-bromann christian-bromann deleted the cb/global-style-support-in-shadow-dom branch May 30, 2025 21:53
@christian-bromann
Copy link
Copy Markdown
Member Author

Docs PR was raised: stenciljs/site#1526
A preview is available at: https://stencil-docs-git-cb-constructable-stylesheet-docs-ionic1.vercel.app/docs/next/styling#constructable-stylesheets
A dev release was made: @stencil/core@4.32.0-dev.1748644071.33363d4

@dgonzalezr mind testing the dev release?

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.

Feature request: allow the use of a stylesheet as adoptedStylesheets in component libraries

1 participant