You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/eidos/THEMING_NOTES.md

169 lines
7.1 KiB

---
title: Eidos Theming — Comparación + decisiones
type: notes
audience: human + agent
status: current
source: extracted from src/uix/eidos/THEMING.md (was §15 + §17)
---
# Eidos Theming — Comparación + decisiones
> Material E3 del theming: cómo se compara eidos con las librerías de referencia,
> y el FAQ de las decisiones polémicas (el porqué de las elecciones). Extraído de
> [`THEMING.md`](./THEMING.md) (referencia E1).
---
## Comparación con librerías de referencia
### Bundle size (app típica con 10 componentes)
| Sistema | Raw | Gzip | Strategy |
|---|---|---|---|
| Tailwind v4 | ~30 KB | ~10 KB | JIT atomic classes |
| **Eidos + purge** | **116 KB** | **13.6 KB** | **JIT custom-property purge** |
| Chakra Panda v3 | ~30 KB | ~12 KB | Build-time JIT |
| Mantine | ~60 KB | ~18 KB | Sin purge |
| Radix Themes | ~80 KB | ~22 KB | Sin purge |
| shadcn/ui | varía | varía | Copy-paste, no centralized |
| **Eidos sin purge** | **218 KB** | **25 KB** | Single CSS |
### Theming features
| Feature | Eidos | Radix Themes | Chakra Panda | Mantine | shadcn | Tailwind v4 |
|---|---|---|---|---|---|---|
| **Token scope as data** | ✅ TSC | ❌ implícito | ⚠️ build-time | ❌ runtime | ❌ N/A | ❌ N/A |
| **Auto-inferencia de deps** | ✅ `var()` parse | ❌ | ✅ types | ❌ | N/A | N/A |
| **Cross-axis collision** | ✅ explícito | ❌ | ⚠️ partial | ❌ | N/A | N/A |
| **Composite scopes** | ✅ `[axis:v, …]` | ❌ | ✅ conditional pairs | ❌ | N/A | N/A |
| **9 color roles canónicos** | ✅ libro | ⚠️ 6 accents | ❌ open | ❌ open | ⚠️ 4 roles | ❌ open |
| **6 size canon coordinated** | ⚠️ canon + guard¹ | ⚠️ 1-3 | ⚠️ 5 | ⚠️ 5 | ❌ | N/A |
| **Density runtime** | ✅ 3 levels | ❌ | ❌ | ⚠️ partial | ❌ | ❌ |
| **Contract introspection** | ✅ typed | ⚠️ docs | ✅ Panda | ⚠️ docs | ❌ | ❌ |
| **Runtime override** | ✅ contract-aware | ⚠️ via CSS vars | ❌ | ✅ CSSVarsProvider | ⚠️ via CSS | ❌ |
| **Persistence versioned** | ✅ envelope | ❌ | ❌ | ❌ | ❌ | N/A |
| **Perceptual layer (sema)** | ✅ unique | ❌ | ❌ | ❌ | ❌ | ❌ |
¹ El bundle `--size-*` existe + emite, pero los componentes NO lo consumen
directamente (re-declaran su mapeo size→fuente). La auditoría 2026-06-15 formalizó
los **3 arquetipos** (`control · compact · dense`, THEMING.md §5) y añadió un **guard
de coherencia** (sin literales px/rem en `font-size-*`/`icon-size-*` de recipe). El
refactor a *consumir* el bundle queda deferido; el guard impide la deriva. Por eso ⚠️
(canon + enforcement) y no ✅ (consumo pleno).
### Mental model
| Sistema | Token philosophy |
|---|---|
| Eidos | 7 layers de indirección, scope-as-contract, perceptual integration |
| Radix Themes | 3 layers, accent runtime swap, no scope contract |
| Chakra Panda | Conditional values build-time, recipe system |
| Mantine | Theme provider runtime, string interpolation |
| shadcn | Plano `--primary` + `.dark`, copy-paste components |
| Tailwind v4 | `@theme` directive, atomic utilities, no tokens compuestos |
**Lectura crítica**: Eidos NO es más simple que Tailwind ni más
ergonómico que shadcn. Es **más expresivo** en la dimensión "qué
puede comunicar un componente". Si tu app solo necesita un color
primario y un dark mode, shadcn es la respuesta. Si tu app necesita
diferenciar perceptualmente entre "guardar borrador" (affirm) y
"borrar permanentemente" (loss) con tokens y animaciones distintas,
Eidos es el sistema.
---
## FAQ — decisiones polémicas
### ¿Por qué el theming no vive en Morfo? ¿No debería Morfo ser source of truth de todo?
Pregunta CRÍTICA — respuesta detallada en [sección 1.bis](#1bis-theming-vive-en-eidos-no-en-morfo-por-diseño).
Resumen: morfo es source-of-truth del **contrato cross-layer** (parts,
events, attrs, archetypes, attr values). Tokens/themes/recipes pertenecen
a Eidos por diseño explícito de la arquitectura (`active_architecture.md`
§9). La regla 2-de-3 lo deriva: los tokens visuales los consume solo
eidos → 1-de-3 → no entra en morfo. La integración eidos↔morfo se hace
**a través del DOM** (los recipes targetean attrs que morfo declara), NO
importando objetos morfo en TS (la regla #6 lo prohíbe explícitamente).
### ¿Por qué 7 capas de indirección? Parece excesivo
Cada capa sirve un override point real:
- Sin capa 1, no puedes traer una paleta Radix custom.
- Sin capa 2, no puedes remapear roles.
- Sin capa 3, no puedes ajustar slots per role.
- Sin capa 4, no puedes overridear un color SOLO para un componente.
- Sin capa 5, no puedes tener palette dinámico por instancia.
- Sin capa 6, no puedes combinar variant × palette.
- Sin capa 7, los recipes mezclan tokens externos con internos.
La capa 4 es la más sospechosa de redundancia. Es candidata a
deprecate si después de 6 meses ningún consumer la usa.
### ¿Por qué TSC y no simplemente convención?
**Convención falla en silencio**. El bug del Toggle (pre-TSC) habría
quedado escondido años. Con TSC, el build falla. Es la diferencia
entre "deberías hacerlo bien" y "no puedes hacerlo mal".
### ¿Por qué no usar Tailwind si es más pequeño?
Tailwind:
- No tiene roles semánticos canónicos (success/danger/warning sí pero
son del mundo Bootstrap).
- No tiene perceptual layer (sema).
- No tiene density runtime.
- No tiene contract introspection programática.
Pero si tu app es simple, **úsalo**. Eidos justifica su complejidad
sólo cuando la app necesita las dimensiones que Eidos cubre.
### ¿Por qué no atomic classes como Tailwind?
Custom properties permiten:
- **Cascada dinámica** (palette overrides en runtime).
- **Theme switching** sin recompile.
- **Persistencia** del user's theme.
- **Composability** con sema (`event:*` scope).
Atomic classes son más comprimibles pero son estáticas. Imposible
hacer `--palette-solid` cambie con `data-color='affirm'` desde
atomic classes sin generar ×N variantes en build.
### ¿Por qué inventar "TSC" y no usar @scope nativo de CSS?
`@scope` (CSS Cascading Modules L6) es bleeding-edge: Chrome 118+,
Firefox 128+, Safari aún no. No es production-ready 2026.
Cuando @scope sea universal, TSC podría re-implementarse encima de
él. La superficie del config (declarations[] + scope) seguiría igual;
sólo cambiaría el CSS emitido.
### ¿Por qué `event:` en TSC y no usar el `data-motion-ref` del doc motion?
Tres razones:
1. **TSC ya existe y funciona**. `data-motion-ref` requeriría un
atributo DOM nuevo, runtime para inyectarlo, registry separado.
2. **Sema ya emite `data-event`**. Reutilizar es 0 coste arquitectónico.
3. **Composable**: `scope: ['event:announce', 'color:affirm']` permite
diferenciar la animación según valencia. `data-motion-ref` perdería
eso o requeriría keys más complejas.
### ¿Cuál es el siguiente paso?
Pendientes deferred:
- Migrar los 5 palette consumers (button, checkbox, switch, radio-group,
toggle-group) a `declarations[]` para uniformidad.
- Implementar el primer caso real de `scope: 'event:*'` (e.g. toast
bg-during-announce).
- Decidir si colapsar la capa 4 (component-color) — defer hasta que
un consumer pida ese punto de extensión.
---

Powered by TurnKey Linux.