|
|
5 months ago | |
|---|---|---|
| .. | ||
| adom | 6 months ago | |
| air | 6 months ago | |
| eidos | 5 months ago | |
| lib | 6 months ago | |
| morfo | 5 months ago | |
| sema | 5 months ago | |
| soma | 5 months ago | |
| terra | 6 months ago | |
| CONTINUITY_2026-04-24.md | 6 months ago | |
| README.md | 5 months ago | |
| active_architecture.md | 5 months ago | |
README.md
UIX
Documento corto de posicionamiento arquitectonico para src/uix.
Vision de conjunto: para entender las cuatro capas (morfo, soma, sema, eidos) en una sola lectura, motivaciones y articulación incluidas, ir a src/uix/active_architecture.md. Este README mantiene la introducción más narrativa.
Nota de continuidad más reciente: 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.
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.
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
SemanticEngine.emit(signal)para que el provider publique ocurrencias - mantener un registry de canales perceptivos (
VisualChannel,SoundChannel,VibraChannel, ...) y despachar la senal a cada uno - semantica secuencial estricta: el
VisualChannel(built-in) materializa la senaldata-event*en el DOM, la mantiene durante elholdconfigurado y la limpia ANTES de que la Promise resuelva
Sema no decide que ocurrio (eso lo decide el provider). Solo orquesta la
ocurrencia que recibe y la despacha a los canales registrados.
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
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 targetapp.dom.remove(target, names)— quita attrs
API publica (servicios reactivos pre-existentes):
viewport,breakpoints,currentBreakpoint,resolve,isAtLeast,matchesBodyScrollLock,DOMContext,RovingFocusGroup
ADom recibe instrucciones ya resueltas. No las interpreta.
uix/lib/dom
Utilidades DOM puras o casi puras.
Aqui viven:
containsgetDocumentgetWindow- 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
SemanticEngine despacha senales a canales perceptivos
VisualChannel materializa la senal en el DOM (data-event*, hold, cleanup)
ADom aplica mutaciones DOM (commit estructural)
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 enmorfo.keyboard.trigger(eventName)— orquesta la secuencia perceptiva + state.
Provider aporta lo que el morfo no puede inferir:
- getters reactivos para
statesyprops - 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.
SemanticEngine recibe la ocurrencia desde runtime.trigger. Genera el id,
despacha la senal a TODOS los canales registrados. El VisualChannel
(built-in) escribe la senal data-event* en el DOM directamente, mantiene los
attrs durante el hold configurado, los limpia y solo entonces resuelve la
Promise (semantica secuencial estricta). Los canales no-visuales (sound,
vibra) son fire-and-forget.
ADom solo aplica el commit estructural posterior. No interpreta. Sema y
ADom son capas hermanas: el engine ya no depende de ADom.
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
// 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.applyescribe, Svelte no lo renderiza.partPropssolo emite identidad estatica (id, marker, ref). - Los handlers de
eventsson 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. Semanticpuede usarDom(dependencia hacia abajo).Domno conoceSemantic.morfo.events.commitses 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
MutationObserverduplicados - 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
EventEmitterglobal 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:
Semaconvirtiendose en runtimeSomaconvirtiendose en engine modalADomconvirtiendose en semantic engineEidosacoplandose 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.
Morfo -> declara contratos
MorfoRuntime -> interpreta morfo dentro de Soma
Provider -> aporta sources, targets, handlers
Effects -> sincronizan state -> attrs
SemanticEngine -> registry + dispatch de senales a canales
VisualChannel -> materializa la senal en el DOM (data-event*, hold, cleanup)
ADom -> aplica mutaciones DOM (commit estructural)
Eidos -> materializa visualmente leyendo DOM
App -> compone servicios
Reglas duras:
Morfono conoceSoma, ni codigo de runtimeMorfoRuntimeleeMorfoy depende deSemaProviderno escribe attrs mutables al DOM directamente; los aporta como sources al runtimeSemanticEngineno conoce DOM. Solo registry + dispatchVisualChannelaccede al DOM directamente (no viaADom); el commit estructural posterior va porADomSemayADomson capas hermanas: ninguna depende de la otraADomno conoceMorfo, niSema, niSoma, niEidosEidosconsume DOM ydata-*, no internals deSomaniSema- Lo que
dom.applyescribe, 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 comodata-archetype="..."porruntime.partProps. - Verbs (24): action verbs canonicos para
morfo.events[].name. Definida ensrc/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:
- src/uix/morfo/README.md — declaracion, archetypes, regla 2-de-3
- src/uix/sema/README.md —
emitcontract, verbs canonicos - src/uix/eidos/README.md — qué consume eidos del DOM (capa por construir)
- src/uix/soma/SOMA_ARCHITECTURE.md — runtime que transcribe morfo
- src/uix/adom/README.md —
dom.apply - src/uix/lib/dom/README.md — primitives DOM puras
- src/uix/terra/README.md — capa anterior, dead branch (referencia)
- 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.