|
|
|
|
|
---
|
|
|
|
|
|
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 |
|
feat(eidos): canonize theming scales + tokenize magic numbers
Audit-driven canonization of the eidos theming layer: a canonical scale that
components bypass with literals drifts into N variants. Every axis is now
retunable-by-theme AND consumed via token, never a bare literal.
Bloque A/B (scales): --blur-* (Tailwind-aligned, depth planes consume it);
inner-shadow --shadow-inset-* (mode-aware) for the recessed plane; gradient
angle/named tokens; --breakpoint-* sourced from ActiveDom (runtime, dev-settable)
+ container queries; opacity dual numeric+semantic scale; border-width
none/thin/medium/thick/heavy (adds real 3px); tracking caps/widest. Fase 7:
3 size archetypes documented + coherence guard (no px/rem in recipe
font-size/icon-size).
Bloque C (magic numbers): z-index 80/50/99 (combobox/nav-menu/drag-drop) ->
recipe content-z/preview-z tokens (overlay micro-band soma mirrors); on-scale
durations -> var(--duration-*) (dialog fast/slow, card slow); 12 single
border/ring widths -> var(--border-width-*), value-preserving. Off-scale kept
only as justified recipe tokens (continuous spinner/loading periods, sub-fast
press). Self-contained dimensional scales (avatar ring, ring-thickness) left whole.
Docs: THEMING.md SS35 + SS6 + SS29, THEMING_NOTES, README, TSC. Excludes
words/palabras/chronos (active dev tracks).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
| **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 | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
|
|
|
|
|
feat(eidos): canonize theming scales + tokenize magic numbers
Audit-driven canonization of the eidos theming layer: a canonical scale that
components bypass with literals drifts into N variants. Every axis is now
retunable-by-theme AND consumed via token, never a bare literal.
Bloque A/B (scales): --blur-* (Tailwind-aligned, depth planes consume it);
inner-shadow --shadow-inset-* (mode-aware) for the recessed plane; gradient
angle/named tokens; --breakpoint-* sourced from ActiveDom (runtime, dev-settable)
+ container queries; opacity dual numeric+semantic scale; border-width
none/thin/medium/thick/heavy (adds real 3px); tracking caps/widest. Fase 7:
3 size archetypes documented + coherence guard (no px/rem in recipe
font-size/icon-size).
Bloque C (magic numbers): z-index 80/50/99 (combobox/nav-menu/drag-drop) ->
recipe content-z/preview-z tokens (overlay micro-band soma mirrors); on-scale
durations -> var(--duration-*) (dialog fast/slow, card slow); 12 single
border/ring widths -> var(--border-width-*), value-preserving. Off-scale kept
only as justified recipe tokens (continuous spinner/loading periods, sub-fast
press). Self-contained dimensional scales (avatar ring, ring-thickness) left whole.
Docs: THEMING.md SS35 + SS6 + SS29, THEMING_NOTES, README, TSC. Excludes
words/palabras/chronos (active dev tracks).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
¹ 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.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|