Aislamiento de CSS
Cada micro-app se renderiza dentro de un shadow root. Esa parte no es configurable: el CSS escrito por una app no puede seleccionar dentro de otra, y los selectores de la página anfitriona no pueden alcanzar su interior.
Lo que sí es configurable es cuáles de los estilos del documento anfitrión
se copian dentro del shadow root para que la app pueda usarlos. Eso es
styleMode.
Dónde se define styleMode
Sección titulada «Dónde se define styleMode»En el wu.json de la app. En ningún otro sitio.
{ "name": "cart", "entry": "src/main.jsx", "styleMode": "fully-isolated"}La razón: cómo se relaciona una app con el CSS del anfitrión es una propiedad de la app, no de la página que la aloja. Una app que trae un sistema de diseño completo debería declararlo una vez, no en cada shell que la incrusta.
Un valor inválido no es fatal: Wu avisa y recurre a shared.
Los tres modos
Sección titulada «Los tres modos»| Valor | Alias | Inyecta en el shadow root |
|---|---|---|
shared |
— | Todos los <style> y <link rel=stylesheet> del documento anfitrión |
isolated |
none |
Nada |
fully-isolated |
own-only |
Solo los estilos que emitió la propia app |
Los alias se añadieron en la v2.0 porque “isolated” y “fully-isolated” describen
un nivel de aislamiento cuando en realidad ambos aíslan igual: la diferencia
está en qué se inyecta. none y own-only lo dicen directamente. Ambas grafías
funcionan.
shared (por defecto)
Sección titulada «shared (por defecto)»<head> anfitrión shadow root┌──────────────┐ ┌────────────────────┐│ tailwind.css │──────►│ tailwind.css ││ tokens.css │──────►│ tokens.css ││ app-x.css │──────►│ app-x.css │└──────────────┘ │ <div wu-app-root> │ │ └────────────────────┘ │ MutationObserver sobre <head> └─► aparece una hoja nueva → se reinyecta (HMR de Vite)Cada hoja de estilos del documento anfitrión se clona o se adopta dentro del
shadow root. Un MutationObserver vigila <head> para que las hojas añadidas
más tarde — que es exactamente lo que hace el HMR de Vite — también se inyecten.
Úsalo cuando las apps comparten un sistema de diseño. El shell carga Tailwind o tus tokens una vez y todas las micro-apps ven las mismas variables.
Coste: cada app lleva una copia del CSS completo del anfitrión. Con muchas apps y una hoja global grande esto es memoria real. También es el modo con más papeletas para dar sorpresas, porque una regla del anfitrión que habías olvidado ahora aplica dentro de una app que no escribiste tú.
isolated (alias none)
Sección titulada «isolated (alias none)»Encapsulación pura de Shadow DOM. No se inyecta nada, no se monta ningún
observador y Wu no vuelve a tocar el estilado de la app. La app es enteramente
responsable de su propio CSS: estilos en línea, CSS-in-JS, módulos CSS incluidos
en el JS, o etiquetas <style> que ella misma añade dentro de su shadow root al
montarse.
Úsalo cuando la app es genuinamente autocontenida. Es el modo más barato y el más predecible.
fully-isolated (alias own-only)
Sección titulada «fully-isolated (alias own-only)»El shadow root recibe solo los estilos que emitió la propia app, ninguno del anfitrión.
Es el modo más delicado, porque “los estilos propios de la app” hay que
inferirlos a partir de etiquetas <style> que ya han aterrizado en el <head>
del anfitrión. Wu las detecta de dos formas:
data-wu-app="<name>"— la convención explícita, agnóstica al bundler.data-vite-dev-idque coincida conpackages/<appName>/src/— servidor de desarrollo de Vite en un monorepo organizado así.
No hay coincidencia genérica por URL. Si tu app no es una app de Vite
organizada como packages/<name>/src/, tienes que etiquetar tus estilos tú
mismo:
import { wu } from 'wu-framework';
const style = document.createElement('style');style.textContent = '/* estilos del carrito */';wu.tagStyleAsApp(style, 'cart'); // pone data-wu-app="cart"document.head.appendChild(style);tagStyleAsApp(el, appName) funciona igual con elementos <style> y <link>.
Llámalo con webpack, esbuild, Rollup, Parcel o etiquetas escritas a mano.
El respaldo es laxo. Si no se encuentra ningún estilo propio en unos 3
segundos, Wu registra un aviso e inyecta todos los estilos cuyo
data-vite-dev-id simplemente contenga el nombre de la app — una coincidencia
por subcadena que puede arrastrar el CSS de una app hermana. Ver en la consola
No own styles found for <app> after timeout, using FALLBACK significa que la
detección falló y que deberías añadir tagStyleAsApp.
Un MutationObserver persistente sigue vigilando <head>, así que los estilos
emitidos más tarde (chunks perezosos, HMR) se recogen. Se desconecta al
desmontar.
Úsalo cuando la app necesita su propio CSS pero no debe heredar el del anfitrión — un widget con tema de Bootstrap dentro de un shell con Tailwind, por ejemplo.
Comparación
Sección titulada «Comparación»shared |
isolated / none |
fully-isolated / own-only |
|
|---|---|---|---|
| El CSS del anfitrión llega a la app | ✅ todo | ❌ | ❌ |
| El CSS propio de la app llega a la app | ✅ | cosa de la app | ✅ autodetectado |
Vigila <head> por estilos nuevos |
✅ | — | ✅ filtrado |
| Necesita una convención de etiquetado | — | — | data-wu-app o estructura Vite |
| Memoria por app | copia completa del CSS anfitrión | ninguna | solo el CSS de la app |
| Por defecto | ✅ |
Qué no cubre el aislamiento
Sección titulada «Qué no cubre el aislamiento»- Las propiedades personalizadas de CSS se heredan a través de la frontera del
shadow. Un
--branddefinido en:rootes visible dentro de todas las apps, en todos los modos. Esto es una ventaja: es la forma más limpia de tematizar apps que por lo demás están totalmente aisladas. - Un
@font-facedeclarado dentro de un shadow root lo ignoran casi todos los navegadores. Declara las fuentes en el documento anfitrión; los archivos de fuente en sí se resuelven sin problema. - Las propiedades heredables siguen heredándose.
font-family,coloryline-heightdefinidos en<body>cruzan al shadow root salvo que la app los sobrescriba. Los estilos base de Wu definenbox-sizingy contención, pero no resetean la tipografía heredada. - La contención de layout cambia el posicionamiento. El contenedor del
sandbox define
contain: layout style painteisolation: isolate, lo que crea un bloque contenedor. Los modales y desplegables que dan por hecho que pueden escapar adocument.bodypueden necesitar ajustes.
Estilos base que Wu siempre inyecta
Sección titulada «Estilos base que Wu siempre inyecta»Sea cual sea el modo, cada shadow root recibe una hoja de estilos pequeña:
:host { display: block; width: 100%; height: 100%; box-sizing: border-box; contain: layout style paint;}.wu-app-root { width: 100%; height: 100%; box-sizing: border-box; isolation: isolate; position: relative; overflow: hidden;}* { box-sizing: border-box; }Fíjate en el overflow: hidden de la raíz de la app: el contenido que se
desborda del contenedor se recorta. Si tu app necesita desbordar (un desplegable
que escapa de su tarjeta), sobrescríbelo desde dentro de la app o dimensiona el
contenedor anfitrión en consecuencia.
Ayuda para depurar: pon el atributo wu-debug en el contenedor anfitrión para
obtener un contorno discontinuo y una etiqueta con el nombre de la app.
Utilidades en tiempo de ejecución
Sección titulada «Utilidades en tiempo de ejecución»wu.tagStyleAsApp(el, 'cart'); // marca un style/link como propiedad de una appwu.getSandboxInfo('cart'); // confirma que la app llegó a montarseSi necesitas forzar una reinyección tras hacer algo inusual con las hojas de
estilo del anfitrión, el sandbox expone reinjectStyles(appName), que respeta el
modo de la app (no hace nada en isolated).
