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

7.1 KiB

title type audience status source
Eidos Theming — Comparación + decisiones notes human + agent current 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 (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.

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.