# ActiveDom `adom` contiene `ActiveDom`: el servicio DOM reactivo de aplicación. ## Qué es hoy Ahora mismo `ActiveDom` no es un bus de eventos semánticos ni un reflector de `data-event*`. Su responsabilidad actual es más pequeña y más concreta: - exponer el ancho de viewport de forma reactiva - resolver el breakpoint actual - mantener la definición de breakpoints de la app - resolver valores responsive - ofrecer helpers de consulta (`isAtLeast`, `matches`) - aplicar/remover atributos DOM de forma controlada (`apply`, `remove`) En otras palabras: ```text libs/dom -> arts/adom -> App.dom / app.dom puro reactivo consumo de app ``` ## Composicion via aapp `createActiveApp(...)` siempre construye `App.dom` (con defaults si no se configura) y se lo pasa a `App.frontend` para que comparta la misma instancia. Ver `$active-app/README.md`. Construir `createActiveDom()` directamente solo es necesario en tests aislados o en consumidores fuera de la composicion estandar. ## Qué pertenece a cada capa ### `libs/dom` Primitives DOM puras o casi puras: - guards y traversal DOM - focus helpers - tabbable helpers - responsive helpers puros **Capa de implementación.** Componentes y soma NO importan de aquí directamente — todo se reexporta a través de `$adom` (ver más abajo). Sólo `arts/adom/*` y los tests de `libs/dom` pueden importar `$libs/dom` directo. No mantiene estado de aplicación. ### `arts/adom` Runtime reactivo de DOM: - `viewport` - `breakpoints` - `currentBreakpoint` - `resolve(...)` - `isAtLeast(...)` - `matches(...)` - `apply(...)` / `remove(...)` como superficie unica de mutacion de atributos - `BodyScrollLock` como helper global de body scroll lock, sin bloquear eventos de puntero - `DOMContext` como helper scoped para `Document` / `ShadowRoot` - `RovingFocusGroup` como helper runtime para navegación compuesta por teclado `ActiveDom` sí mantiene estado reactivo y por eso vive aquí, no en `$libs/dom`. ## Posición en App `ActiveDom` vive a nivel de aplicación: ```ts App.dom; app.dom; ``` La implementación se consume desde la capa de aplicación y desde artefactos que necesitan escribir atributos finales sobre un target DOM, por ejemplo `fend`. ## API actual La API pública real de `ActiveDom` hoy es esta: ```ts export type ActiveDom = { breakpoints: Active; viewport: { readonly width: number }; currentBreakpoint: Active; resolve(value: ResponsiveProp | undefined): T | undefined; isAtLeast(breakpoint: Breakpoint): boolean; matches(breakpoint: Breakpoint): boolean; apply(change: StructuralChange): void; remove(target: HTMLElement, names: readonly string[]): void; dispose(): void; }; ``` Creación: ```ts const dom = createActiveDom({ breakpoints: readableActive(() => ({ lg: 1100 })) }); const domWithDefaults = createActiveDom(); ``` Responsive: ```ts const columns = dom.resolve({ base: 1, sm: 2, lg: 3 }); const tone = dom.resolve({ base: 'compact', md: 'normal', xl: 'wide' }); ``` Mutacion DOM: ```ts dom.apply({ target: node, attrs: { 'data-state': 'open', 'aria-busy': true, 'data-hidden': false } }); dom.remove(node, ['data-state', 'aria-busy']); ``` Reglas: - los breakpoints se definen en la creación del `dom` - `breakpoints` es opcional; si no se pasa, usa `BREAKPOINTS_DEFAULT` - no hay herencia de `dom` padre - `ActiveDom` es servicio de app, no scope anidado - `ActiveDom` registra el listener de `resize` al construirse (por instancia). El singleton compartido (`shareViewport: true`) pospone la asignación del listener a la primera lectura del viewport — importar `$adom` no aloca estado reactivo si nadie consume el viewport. - `apply` solo escribe atributos; no interpreta semantica ni eventos - `false | null | undefined` remueven atributos - `viewport` es **per-instancia por defecto**: cada `ActiveDom` posee su propio listener de `resize`, scoped al `targetWindow` (default `window`). Esto evita filtraciones entre tests, iframes, popups y entornos happy-dom. Llamar `dispose()` desadjunta ese listener. ### Compartir el viewport entre instancias Para apps "single window" donde toda la composición vive en el mismo documento, opt-in al singleton evita N listeners para el mismo evento: ```ts const dom = createActiveDom({ shareViewport: true }); ``` Bajo esta opción, `dispose()` es no-op para el viewport (el singleton vive toda la vida del proceso). Lo que sí se libera es `breakpoints`, `currentBreakpoint` y los demás reactivos por-instancia. ### Tracking de un window distinto Para iframes, popups o entornos de test: ```ts const iframeDom = createActiveDom({ targetWindow: iframe.contentWindow! }); const popupDom = createActiveDom({ targetWindow: popup }); ``` Ignorado cuando `shareViewport: true` (el singleton siempre rastrea el top-level `window`). ## Qué no es `ActiveDom` no es: - `SemanticEngine` - broker de eventos - reflector de `data-event*` - hub de `MutationObserver` - sistema de theme - sistema de modal, backdrop o inert - reemplazo de `$libs/dom` Además, `uix/adom` puede alojar helpers DOM con estado global real, como `BodyScrollLock`, o helpers scoped de runtime como `DOMContext`, cuando ya no son primitives puras de `$libs/dom` pero tampoco pertenecen a un componente UI concreto. También caben aquí helpers runtime de foco con estado propio, como `RovingFocusGroup`, que reutilizan `$libs/dom` por debajo pero ya no son solo utilidades puras. ## Pagina De Prueba La pagina manual esta en `/test/adom` y cubre: - viewport y breakpoint actual - `resolve()` responsive - `apply()` / `remove()` - `BodyScrollLock` - `DOMContext` - `RovingFocusGroup` ## Relación con otras piezas ### Semántica La semántica pertenece a `Sema` y a `SemanticEngine`, no a `ActiveDom`. Si mañana `ActiveDom` refleja eventos al DOM, será como consumidor de `SemanticEngine`, no como autoridad semántica. ### Theme El theme no pertenece a `dom`. Va en `app.presentation`, porque es estado de presentación de aplicación, no una primitive DOM. ### Air y Terra `air` y `terra` no consumen esta capa nueva. Su código actual sirve como referencia histórica para extraer utilidades hacia `$libs/dom`, pero no forman parte del runtime nuevo. ## Estado del diseño `ActiveDom` está en fase fundacional. Lo que ya está cerrado: - `app.dom` - `App.dom` - `viewport` - `breakpoints` - `currentBreakpoint` - resolución responsive Lo que queda para fases posteriores, si de verdad hace falta: - reflexión de eventos semánticos al DOM - observers compartidos - APIs por `Document` o `ShadowRoot` - introspección/diagnóstico de runtime más rica La regla importante por ahora es simple: > `ActiveDom` es el servicio reactivo de DOM de la app; `$libs/dom` es su base pura.