# UIX Documento corto de posicionamiento arquitectonico para `src/uix`. Nota de continuidad más reciente: [src/uix/CONTINUITY_2026-04-24.md](/G:/dev/svelte/vicen/src/uix/CONTINUITY_2026-04-24.md) UIX no intenta ser "otra libreria de componentes". La apuesta es mas ambiciosa y mas estructural: **separar capas que casi todos los frameworks actuales mantienen mezcladas**. En la mayoria de sistemas de UI, estas cosas viven pegadas: - contrato publico del DOM - comportamiento headless - accesibilidad - semantica del evento - capa visual - motores modales (sound, vibra, motion) - integracion con servicios de app UIX intenta partir ese bloque en piezas con fronteras fuertes. --- ## 1. La idea central UIX modela la interfaz como varias capas cooperando, no como un unico componente gigante que hace todo a la vez. ```text App ├─ servicios transversales │ └─ ADom └─ componentes └─ capa estructural / semantica / comportamental / visual ``` La intuicion es esta: - la estructura publica del componente no es lo mismo que su comportamiento - la semantica de un evento no es lo mismo que su materializacion - el DOM activo no es lo mismo que utilidades DOM puras - la app no deberia acoplar motores modales entre si UIX pone nombres y contratos explicitos a esas separaciones. --- ## 2. Las capas de UIX ### `Morfo` Contrato estructural cross-layer del componente. Define: - partes - `data-*` - ARIA - foco - teclado - eventos No es prose ni runtime. Es la forma canonica publica del componente. Ver: [src/uix/morfo/README.md](/G:/dev/svelte/vicen/src/uix/morfo/README.md) ### `Sema` Vocabulario semantico y canalizador de senales perceptivas. Su trabajo: - definir las familias canonicas (`contact | commit | alert | handle | emerge | sustain`) - definir los intents canonicos (`neutral | affirm | fulfill | risk | threat`) - exponer `EngineSemantic.emit(event)` para que el provider publique ocurrencias - garantizar la secuencia perceptiva: escribir senal `data-event*`, esperar 1 rAF para que CSS la observe, resolver, mantener y limpiar `Sema` no decide que ocurrio (eso lo decide el provider). Solo orquesta la ocurrencia que recibe. Ver: [src/uix/sema/README.md](/G:/dev/svelte/vicen/src/uix/sema/README.md) ### `Soma` Capa headless de comportamiento. Gestiona: - estado - contexto - a11y - keyboard / pointer / focus - emision de eventos hacia `ADom` `Soma` no deberia conocer la implementacion concreta de los engines modales. Su trabajo es emitir hechos del componente, no materializarlos. Ver: [src/uix/soma/SOMA_ARCHITECTURE.md](/G:/dev/svelte/vicen/src/uix/soma/SOMA_ARCHITECTURE.md) ### `Eidos` Capa visual. Reacciona a contratos DOM y a senales reflejadas, pero no implementa la logica headless del componente. Su responsabilidad es apariencia, no comportamiento. ### `ADom` Runtime DOM activo de aplicacion. No es semantic engine. No conoce Morfo, ni Sema, ni Soma, ni Eidos. Su unica funcion es **coordinar y sincronizar mutaciones DOM**. API publica (mutaciones): - `app.dom.apply(change)` — aplica un paquete de attrs sobre un target - `app.dom.remove(target, names)` — quita attrs API publica (servicios reactivos pre-existentes): - `viewport`, `breakpoints`, `currentBreakpoint`, `resolve`, `isAtLeast`, `matches` - `BodyScrollLock`, `DOMContext`, `RovingFocusGroup` ADom recibe instrucciones ya resueltas. No las interpreta. Ver: [src/uix/adom/README.md](/G:/dev/svelte/vicen/src/uix/adom/README.md) ### `uix/lib/dom` Utilidades DOM puras o casi puras. Aqui viven: - `contains` - `getDocument` - `getWindow` - foco - traversal - wrappers base de observers No contiene el runtime activo. Ese papel pertenece a `ADom`. --- ## 2.bis Como se ejecuta un componente La arquitectura cerrada (post-2026-04-25) define seis piezas con responsabilidades disjuntas. Ninguna invade a la siguiente. ``` Morfo declara MorfoRuntime transcribe Provider aporta sources, targets y handlers Effects sincronizan attrs derivados EngineSemantic emite senales perceptivas ADom aplica mutaciones DOM ``` ### El reparto operativo `Morfo` es DNA: un fichero por componente que declara `parts`, `data-*`, `aria-*`, `role`, `keyboard`, `focus`, `events`. No ejecuta nada. `MorfoRuntime` (en `soma/`) interpreta el morfo. Una instancia por componente recibe del provider las fuentes de estado, los targets DOM y los handlers de eventos. Expone: - `partProps(part)` — devuelve solo identidad estatica del nodo (id, marker, ref attachment). Nada mutable. - `attachPart(part, target)` — el provider registra el nodo DOM real cuando monta. - `keydown(part, event)` — dispatch de teclas declaradas en `morfo.keyboard`. - `trigger(eventName)` — orquesta la secuencia perceptiva + state. `Provider` aporta lo que el morfo no puede inferir: - getters reactivos para `states` y `props` - getters reactivos para los `parts` (ids dinamicos) - handlers sincronos para los `events` - glue de layers ortogonales (Presence, Dismissal, ScrollLock — no son morfo) `Effects` (registrados por el runtime al montar) escuchan cambios en los sources y aplican los attrs derivados via `dom.apply`. `EngineSemantic` recibe el evento desde `runtime.trigger`. Escribe la senal `data-event*` via `dom.apply`, espera 1 rAF, resuelve, mantiene la senal el hold configurado y limpia. `ADom` solo aplica. No interpreta. ### La secuencia de `runtime.trigger(eventName)` ``` 1. prewrite imperativo (transient markers como data-last-action) 2. await semantic.emit(event) 3. handler sincrono del provider muta state 4. effects derivan y aplican attrs estructurales (data-state, aria-*) ``` El handler muta state. Los effects ven el cambio y reescriben el DOM. ADom es el unico escritor de attrs mutables. ### Tres escenarios de Soma ```ts // Cambio estructural sin senal provider.commitState(change) // internamente: dom.apply(change) // Cambio estructural con senal provider.commitState(change, event) // internamente: await semantic.emit(event); dom.apply(change) // Senal sin cambio estructural provider.emitEvent(event) // internamente: void semantic.emit(event) ``` ### Reglas operativas - Lo que `dom.apply` escribe, Svelte no lo renderiza. `partProps` solo emite identidad estatica (id, marker, ref). - Los handlers de `events` son sincronos. Async va fuera del trigger. - Los guards (`if (disabled) return`) van en el call-site, no dentro del handler — si entran al handler, ya emitieron senal perceptiva. - `Semantic` puede usar `Dom` (dependencia hacia abajo). `Dom` no conoce `Semantic`. - `morfo.events.commits` es descriptivo: documenta lo observable, no lo ejecuta. La cadena causal real es handler -> state -> effect. --- ## 3. Que hace distinto a UIX ### 3.1 El contrato estructural es una capa propia En la mayoria de librerias, la estructura publica del componente esta dispersa: - atributos en el provider - roles en el render - partes en CSS - selector names en docs - contratos en tests UIX intenta concentrar eso en `Morfo`. Eso no es una comodidad menor; cambia el tipo de sistema que puedes construir: - docs derivadas del contrato - validacion cross-layer - menos drift entre headless y visual - tooling mas fiable ### 3.2 La semantica no se mezcla con la ejecucion UIX separa el **nombre de la ocurrencia** de su **materializacion modal**. Eso permite que: - sonido - vibracion - CSS - motores futuros usen el mismo vocabulario sin quedar pegados entre si. ### 3.3 El comportamiento headless no carga con toda la modalidad `Soma` no deberia ser un mega-engine que sabe de todo: - no sabe reproducir WAVs - no sabe vibrar - no sabe decidir la fisica perceptiva de cada canal `Soma` emite. Los engines ejecutan. ### 3.4 El DOM activo es infraestructura de app, no detalle incidental Muchos sistemas tratan el DOM como detalle local del componente. UIX da un paso mas: reconoce que hay hechos transversales del DOM que varios consumidores quieren escuchar, y por eso introduce `ADom`. Eso permite: - un solo punto de publicacion - listeners tipados - reflection uniforme en atributos - menos `MutationObserver` duplicados - mejor tooling y debug ### 3.5 La app compone servicios, no "super componentes" UIX se apoya en un modelo donde la app compone servicios transversales y los componentes los consumen. `langs`, `presentation`, `logger` y `ADom` viven mejor como servicios de app que como dependencias ocultas dentro de cada componente. --- ## 4. Lo que UIX no es UIX no es: - una coleccion plana de componentes visuales - un simple wrapper opinionated sobre primitives existentes - un design system clasico donde visual, comportamiento y contratos viven juntos - un semantic engine centralizado que ejecuta todas las modalidades - un `EventEmitter` global disfrazado de arquitectura Tampoco busca novedad gratuita. La originalidad de UIX no esta en inventar nombres exoticos, sino en **separar problemas reales** que otros sistemas suelen aceptar como un unico bloque. --- ## 5. Comparacion honesta con otros enfoques ### Frente a headless libraries clasicas Librerias como Radix, Ariakit o React Aria resuelven muy bien comportamiento y accesibilidad. Pero normalmente no separan: - contrato estructural declarativo - vocabulario semantico independiente - runtime transversal de DOM activo UIX quiere cubrir ese espacio. ### Frente a design systems clasicos Muchos design systems tienen tokens, componentes y guidelines, pero la frontera entre: - estructura - comportamiento - visualidad - semantica queda difusa. UIX intenta que cada una tenga una capa reconocible. ### Frente a engines modales aislados Es relativamente comun encontrar sistemas de motion o sound por separado. Lo raro es tener: - headless primitives - contrato estructural machine-readable - vocabulario comun - servicio de DOM activo - engines modales desacoplados trabajando juntos sin colapsar en un runtime monolitico. --- ## 6. Por que esto puede ser valioso Si sale bien, UIX ofrece algo poco comun: - mejor explicabilidad arquitectonica - menos drift entre capas - mas capacidad de validacion automatica - mejor testabilidad - mas libertad para introducir nuevos engines - mas honestidad sobre que pertenece al framework y que pertenece al integrador Especialmente importante: **la coherencia cross-modal puede tratarse como responsabilidad del integrador, no como una falsa promesa de un runtime centralizado que pretende saberlo todo.** El framework puede proveer: - vocabulario - contratos - transporte - puntos de extension Pero no debe fingir que puede decidir por todas las modalidades de todas las apps. --- ## 7. Los riesgos reales UIX tambien tiene riesgos claros, y conviene decirlos sin adornos. ### 7.1 Exceso de capas Si las fronteras no estan clarisimas, el sistema puede sentirse mas complejo de lo que realmente resuelve. ### 7.2 Nombres sin disciplina Si `Morfo`, `Sema`, `Soma`, `Eidos`, `ADom` no mantienen contratos nitidos, los nombres se convierten en decoracion y no en arquitectura. ### 7.3 Invasion de responsabilidades El peligro constante es que una capa intente hacer el trabajo de otra: - `Sema` convirtiendose en runtime - `Soma` convirtiendose en engine modal - `ADom` convirtiendose en semantic engine - `Eidos` acoplandose a detalles incidentales UIX solo funciona si cada capa acepta sus limites. ### 7.4 Falta de precedentes No hay demasiados sistemas con esta composicion exacta. Eso significa mas libertad, pero tambien menos patrones externos que copiar. Hay que inventar con disciplina. --- ## 8. Reglas de dependencia UIX preserva una direccion clara de acoplamiento. ```text Morfo -> declara contratos MorfoRuntime -> interpreta morfo dentro de Soma Provider -> aporta sources, targets, handlers Effects -> sincronizan state -> attrs EngineSemantic -> emite senales perceptivas (depende de Dom) ADom -> aplica mutaciones DOM Eidos -> materializa visualmente leyendo DOM App -> compone servicios ``` Reglas duras: - `Morfo` no conoce `Soma`, ni codigo de runtime - `MorfoRuntime` lee `Morfo` y depende de `Dom` y `Semantic` - `Provider` no escribe attrs mutables al DOM directamente; los aporta como sources al runtime - `EngineSemantic` puede usar `Dom` (hacia abajo); `Dom` no conoce `Semantic` - `ADom` no conoce `Morfo`, ni `Sema`, ni `Soma`, ni `Eidos` - `Eidos` consume DOM y `data-*`, no internals de `Soma` ni `Sema` - Lo que `dom.apply` escribe, Svelte no lo renderiza ### Regla 2-de-3 para extender Morfo Una extension a Morfo solo se justifica si **al menos dos de las tres capas** (soma, sema, eidos) la consumen. Si solo soma se beneficia, el patron es virtual prop en provider — no extender el contrato. Bajo esa regla: | Extension | Soma | Sema | Eidos | En morfo | |---|---|---|---|---| | `parts[].archetype` | ✅ emit | ✅ verbs por rol | ✅ selectores transversales | ✅ | | `events[].semantic` | ✅ payload | ✅ vocabulario | ✅ tinta | ✅ | | `events[].prewrite` | ✅ ejecuta | ✅ secuencia | ✅ tinta exit | ✅ | | `firstOf` value source | ✅ only | — | — | ❌ | | `prop-not-nullish` cond | ✅ only | — | — | ❌ | | Field-context OR | — virtual prop | — | — | ❌ | ### Vocabularios canonicos cross-layer - **Archetypes** (24): clasificacion de partes cross-component. Definida en `src/uix/morfo/types.ts:ARCHETYPE_VOCABULARY`. Emitida como `data-archetype="..."` por `runtime.partProps`. - **Verbs** (24): action verbs canonicos para `morfo.events[].name`. Definida en `src/uix/sema/verbs.ts:SEMA_VERBS`. Convencion para composite names: `{verb}-{variant}` (e.g. `commit-save`, `dismiss-outside`). --- ## 9. La diferencia en una frase Si hubiera que resumir UIX en una sola idea, seria esta: > Morfo declara, MorfoRuntime transcribe, Provider aporta, Effects sincronizan, > Semantic emite, Dom aplica. Seis piezas, seis responsabilidades, ninguna invade a la siguiente. --- ## 10. Orden de lectura sugerido Para entender el sistema en su estado actual: 1. [src/uix/morfo/README.md](/G:/dev/svelte/vicen/src/uix/morfo/README.md) — declaracion, archetypes, regla 2-de-3 2. [src/uix/sema/README.md](/G:/dev/svelte/vicen/src/uix/sema/README.md) — `emit` contract, verbs canonicos 3. [src/uix/eidos/README.md](/G:/dev/svelte/vicen/src/uix/eidos/README.md) — qué consume eidos del DOM (capa por construir) 4. [src/uix/soma/SOMA_ARCHITECTURE.md](/G:/dev/svelte/vicen/src/uix/soma/SOMA_ARCHITECTURE.md) — runtime que transcribe morfo 5. [src/uix/adom/README.md](/G:/dev/svelte/vicen/src/uix/adom/README.md) — `dom.apply` 6. [src/uix/lib/dom/README.md](/G:/dev/svelte/vicen/src/uix/lib/dom/README.md) — primitives DOM puras 7. [src/uix/terra/README.md](/G:/dev/svelte/vicen/src/uix/terra/README.md) — capa anterior, dead branch (referencia) 8. [src/uix/air/README.md](/G:/dev/svelte/vicen/src/uix/air/README.md) — capa visual anterior, dead branch (referencia) La arquitectura final seguira cambiando, pero esta es la idea fundacional que explica por que UIX no se parece demasiado a otros frameworks de UI.