Stencil FOUC styles should use @layer to avoid cascade conflicts with React 19

Abierto Apto para principiantes
#6,649 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

@johnjenkins ya está trabajando en esto.

Desde el 29/3/2026.

  • #6657 de @johnjenkins — abierto

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
68/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Tranquilo
Stack tecnológico
css, typescript
Área
frontend, web-dev

Línea de trabajo

Empieza en src/runtime/bootstrap-lazy.ts e inspecciona cómo se rellena el elemento data-styles y cómo se inserta en el head. Incluye el FOUC CSS en la @layer con nombre descrita por el issue y, después, verifica que los estilos generados sigan teniendo una prioridad baja cuando React 19 reordena los estilos del head, y que los componentes hidratados y las reglas de visibilidad del consumidor se comporten correctamente.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Stencil v5

Summary

Stencil's FOUC prevention CSS (<style data-styles>) can override consumer CSS when used with React 19, because React 19 reorders <head> elements and changes the cascade order. Wrapping the FOUC styles in @layer would make them lowest-priority by default, fixing the conflict without requiring consumers to patch Stencil.

Current behavior

In bootstrap-lazy.ts, Stencil injects a <style data-styles> tag into <head> with:

market-component-a, market-component-b, ... { visibility: hidden }
[hydrated] { visibility: inherit }

It's inserted via:

head.insertBefore(dataStyles, metaCharset ? metaCharset.nextSibling : head.firstChild);

This relies on source order in <head> — the FOUC styles are placed early so that component-specific and consumer CSS loaded later will override them.

Problem with React 19

React 19 introduced "hoistable" style management — it moves <link rel="stylesheet"> tags to head.firstChild. This pushes <meta charset> down in the head, so when Stencil inserts after metaCharset.nextSibling, the FOUC styles end up after component CSS <link> tags instead of before them.

The result: [hydrated] { visibility: inherit } and consumer class selectors (e.g. .popover { visibility: hidden }) have equal specificity (0,1,0). With the FOUC style now later in source order, it wins — causing components to inherit visibility: hidden from ancestors even after hydration.

This caused a production incident at Square where Market web component popovers became invisible but still captured pointer events, blocking user interaction across the entire dashboard.

Proposed fix

Wrap the FOUC CSS in @layer:

// src/runtime/bootstrap-lazy.ts
- dataStyles.textContent += cmpTags + HYDRATED_CSS;
+ dataStyles.textContent += `@layer stencil-hydration{${cmpTags}${HYDRATED_CSS}}`;

Per the CSS cascade spec, layered styles always lose to unlayered styles, regardless of source order or specificity. This is semantically correct — Stencil's FOUC styles are intentionally low-priority defaults that should yield to any consumer CSS.

Why @layer is the right solution
  • Order-independent: Works regardless of where the <style> tag lands in <head>
  • Semantically correct: FOUC styles are defaults, not overrides
  • No consumer changes needed: Consumers don't need to patch Stencil
  • Framework-agnostic: Fixes the issue for React 19 and any future framework that reorders <head>
  • Browser support: @layer is supported in all modern browsers (Chrome 99+, Firefox 97+, Safari 15.4+). Older browsers silently discard @layer blocks — but the FOUC bug only manifests with React 19 which itself requires modern browsers

Workaround

We're currently working around this by patching Stencil's output at runtime with a MutationObserver that wraps <style data-styles> content in @layer stencil-hydration { ... } before MFEs load.

Environment

  • Stencil: 4.18.0
  • React: 19.x
  • Output target: dist-custom-elements (via @stencil/react-output-target)
Lenguaje dominante
TypeScript
Estrellas
13.2k
Forks
865
Merge medio
2 d 16 h
PR fusionados (30 d)
29

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de stenciljs/core

Todos los issues de stenciljs/core

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.