diff --git a/optimize-bundle.md b/optimize-bundle.md index 47c2854f1..9007b5661 100644 --- a/optimize-bundle.md +++ b/optimize-bundle.md @@ -62,7 +62,7 @@ monta la página. Ese eje decide la Fase 1 (`@layer` o no). | Fase | Qué | Riesgo | Verifica | | --- | --- | --- | --- | | **0** ✅ | Des-duplicar los 10 (quitar de `index.css`; ya auto-importan) | nulo | hecho — ver resultado | -| **1** | Piloto `dialog` (compuesto + portalizado): dónde va el import · tokens cross-portal · **decidir `@layer`** | medio | build + navegador | +| **1** ✅ | Piloto `dialog` (compuesto + portalizado): import en la raíz · tokens cross-portal OK · **`@layer` descartado** | medio | hecho — ver resultado | | **2** | Partials compartidos (`menu-indicator`) + primitivas layout se quedan en foundation; auditar colisiones a igual especificidad | medio | eidos-lint | | **3** | Propagar a los ~94 restantes **por lotes** por familia (overlays·pickers·menús·forms·data·tipografía); nunca en cascada | medio | build+lint+visual/lote | | **4** | `index.css` = foundation-only; medir CSS/página | bajo | build | @@ -82,6 +82,40 @@ chunk carga el recipe). Repetir esta auditoría en cada lote de la Fase 3. dejan solo su contrato de token de `base.css`; textarea deja solo la referencia de `words.css`. Build verde, sin acoplamiento roto. +### Decisión `@layer` (Fase 1) — DESCARTADO + +Evaluado con evidencia, no en teoría. **No se adopta `@layer`**: + +- **0** usos actuales en eidos → sería un sistema nuevo sobre las 104 recipes. +- **16** `!important` en 5 recipes → `@layer` invierte su precedencia (en cascada + con capas, `!important` resuelve en orden de capa **inverso**) ⇒ regresiones + silenciosas a auditar una a una. +- Cada recipe ya scopea a `[data-{component}]` (doctrina CLAUDE.md) → las + colisiones cross-componente a igual especificidad están **estructuralmente + prevenidas**. La única dependencia de orden real (`menu-indicator`, partial + compartido) se resuelve dejándolo en foundation (Fase 2). + +El scoping por `data-*` ya da el determinismo que daría `@layer`, sin su coste +(envolver 104 ficheros) ni su riesgo (invertir 16 `!important`). `@layer` queda +como **escape hatch documentado** si algún día aparece una colisión que el +scoping no resuelva. + +### Fase 1 — resultado medido (piloto `dialog`) + +- **Patrón fijado para compuestos**: el `import './x.css'` va en el `.svelte` + **raíz** (`dialog.svelte`). El barrel (`index.ts`) importa la raíz, así que el + CSS carga al usar cualcomponente parte (Trigger, Content portalizado, …). +- `index.css`: 94 → **93** `@import`. Monolito **814 420 → 805 692 raw** + (−8.7 KB) · **108 991 → 107 843 gz** (−1.1 KB). +- Recipe fuera del monolito: `[data-dialog-content]` / `[data-dialog-overlay]` + = **0**; viaja en su chunk. El token `--dialog-content-bg` se queda en + `base.css` (global). +- **Cross-portal verificado en navegador**: el `[data-dialog-content]` + portalizado (fuera de `[data-dialog]`) sigue **completamente estilado** + (bg `oklch(0.285 0 0)`, radius 16px, sombra, padding 20px) — tokens de + `base.css` global + recipe del chunk. El portal no rompe nada. +- `check` 0 errores. + ## Metodología - `npm run build` → `adapter-static`, salida en `build/`. diff --git a/src/uix/eidos/components/dialog/dialog.svelte b/src/uix/eidos/components/dialog/dialog.svelte index ffa31f0c2..6ebb8a039 100644 --- a/src/uix/eidos/components/dialog/dialog.svelte +++ b/src/uix/eidos/components/dialog/dialog.svelte @@ -19,6 +19,7 @@ * * */ + import './dialog.css'; import * as Dialog from '$soma/components/dialog'; import type { DialogProps } from './types'; diff --git a/src/uix/eidos/index.css b/src/uix/eidos/index.css index a9f87fbda..19ea9aa3c 100644 --- a/src/uix/eidos/index.css +++ b/src/uix/eidos/index.css @@ -64,10 +64,9 @@ /* * Code-split components are intentionally NOT aggregated here: they import * their own CSS from their `.svelte`, so Vite emits a per-component chunk that - * loads only when the component mounts. Do NOT re-add their `@import` here or - * the CSS double-loads (aggregate + chunk). Currently code-split: - * badge · card · image · password-field · s-text · s-text-virtual-list · - * scroll-frames · skeleton · spinner · textarea + * loads only when the component mounts. The ABSENCE of a component's `@import` + * below means it is code-split — do NOT re-add it (the CSS would double-load: + * aggregate + chunk). Migration tracker + plan: optimize-bundle.md. */ @import './components/box/box.css'; @import './components/flex/flex.css'; @@ -98,7 +97,6 @@ @import './components/avatar/avatar.css'; @import './components/breadcrumb/breadcrumb.css'; @import './components/toast/toast.css'; -@import './components/dialog/dialog.css'; @import './components/alert-dialog/alert-dialog.css'; @import './components/dropdown-menu/dropdown-menu.css'; @import './components/context-menu/context-menu.css';