--- title: Guía de implementación — Semántica perceptiva en UIX type: notes audience: human + agent authority: historical seed — superseded by docs/CANON.md + code; kept for the Spanish narrative status: historical source: migrated verbatim from src/docs/GUIA_IMPLEMENTACION_SEMAUIX.md (2026-07-02, docs-book F7.6; historical seed — not translated) --- # Guía de implementación — Semántica perceptiva en UIX > **Estado: semilla histórica, NO autoritativa.** Este documento fue la guía > fundacional de la migración semántica (2026-05). El canon vigente es > [`docs/CANON.md`](../CANON.md) + el código (`SEMA_MAP`, `holds.ts`, > los morfos); las desviaciones y extensiones registradas viven en > [`book-deviations.md`](./book-deviations.md). > Se conserva por su narrativa en castellano. Donde este texto discrepe del > canon o del código, **el canon y el código ganan**. (CLAUDE.md aún lo cita > como autoritativo; esa cita se actualizará en el pase diferido de CLAUDE.md.) ## De la teoría del libro a la arquitectura del framework Este documento traduce las decisiones del libro *Semántica perceptiva de la interfaz* a la arquitectura UIX (Morfo/Soma/Sema/Eidos). No repite la teoría — la convierte en contratos, vocabularios, reglas de resolución y convenciones técnicas. --- ## 1. Vocabulario canónico corregido > **Canonical (EN):** [`docs/CANON.md`](../CANON.md) is now the single > source of truth for families / intents / verbs, anchored to the book + code. > This section is kept for the Spanish narrative; if it disagrees with the canon, > the canon wins. ### 1.1. Familias 8 familias. Sin excepciones. ```ts export const SEMA_FAMILIES = [ 'contact', // ¿el sistema ha sentido mi acción? 'commit', // ¿algo quedó fijado o tuvo consecuencia? 'signal', // ¿algo reclama mi atención? 'handle', // ¿estoy manipulando directamente un objeto? 'emerge', // ¿algo entró o salió del campo perceptivo? 'shift', // ¿cambió el marco operativo? 'sustain', // ¿esto sigue ocurriendo? 'delegate' // ¿quién actúa ahora? (libro cap. 29) ] as const; ``` **Cambios respecto a la implementación actual:** | Antes | Ahora | Razón | |---|---|---| | `alert` como familia | `signal` con verbo `alert` | alert es intensidad dentro de signal, no familia | | sin `shift` | `shift` añadido | emerge ≠ shift — dropdown ≠ modal | | sin `loss` | `loss` como intent | threat ≠ loss — amenaza ≠ pérdida consumada | ### 1.2. Intents 6 intents. Regiones evaluativas del espacio valencia/activación. Fuente única: `src/uix/intent.ts` exporta `INTENTS` y `Intent`. Las capas no redeclaran ni prefijan este vocabulario. - `neutral`: sin carga evaluativa fuerte - `affirm`: confirmación positiva, baja activación - `fulfill`: resolución positiva, mayor activación - `risk`: problema corregible, negativo moderado - `threat`: amenaza activa, alta activación negativa - `loss`: pérdida consumada, consecuencia ya ocurrida ### 1.3. Verbs por familia ```ts export const SEMA_VERBS = { contact: [ 'press', 'tap', 'activate', 'focus', 'trigger', 'release' ], commit: [ 'select', 'unselect', 'toggle', 'save', 'submit', 'confirm', 'complete', 'fail', 'cancel', 'reset', 'discard', 'delete', 'restore', 'expire', 'acknowledge', 'apply', 'partial', 'block', 'move', 'set', 'remove', 'reorder', 'upload' ], signal: [ 'announce', 'notify', 'warn', 'alert', 'inform', 'emphasize', 'remind' ], handle: [ 'pick', 'carry', 'drop', 'drag', 'resize', 'reorder', 'rotate', 'scroll', 'zoom' ], emerge: [ 'present', 'dismiss', 'open', 'close', 'expand', 'collapse', 'reveal', 'hide' ], shift: [ 'enter-mode', 'exit-mode', 'navigate', 'route', 'step', 'return', 'context' ], sustain: [ 'start', 'progress', 'loading', 'waiting', 'syncing', 'processing', 'streaming', 'pending', 'retrying', 'upload', 'end' ], delegate: [ 'offer', 'plan', 'authorize', 'act', 'review', 'escalate', 'return' ] } as const; ``` **Cambios de verbs:** | Antes | Ahora | Razón | |---|---|---| | `contact.select` | `commit.select` | seleccionar fija estado = commit | | `contact.toggle` | `commit.toggle` | alternar fija estado = commit | | `handle.acknowledge` | `commit.acknowledge` | reconocer cierra algo = commit | | `handle.edit` | `shift.enter-mode` | editar cambia régimen = shift | --- ## 2. Sistema de 8 tokens de color > **Vigente**: el inventario real es de **9 roles** — se añadió `tertiary` > (jerarquía) — y los slots CSS reales son `--color-{role}-{slot}` > (`solid`, `text`, `bg`, `border`, …), no `--color-{role}-element`. > Fuente: [`theming/reference.md`](../theming/reference.md) §4 + > `THEME_BASE_COLOR_ROLES` (`src/uix/eidos/themes/base.ts`). La doctrina de > dos ejes (jerarquía vs intent) que sigue es la vigente; los nombres > concretos evolucionaron. ### 2.1. Los 8 valores Dos ejes ortogonales: **Jerarquía** (sin carga evaluativa): - `primary` — acción principal - `secondary` — acción secundaria **Intent** (carga evaluativa): - `neutral` — sin juicio fuerte - `affirm` — confirmación suave - `fulfill` — objetivo cumplido - `risk` — problema corregible - `threat` — amenaza activa - `loss` — pérdida consumada ### 2.2. Regla de resolución intent ↔ color ```ts visualColor = intent !== 'neutral' → intent // la carga evaluativa gana intent === 'neutral' → color ?? 'neutral' // jerarquía si se pasó, neutral si no ``` Un componente recibe dos props ortogonales con prioridad clara: - `intent` (semántico) — siempre presente, default `neutral`. Decide firma sema y, si es evaluativo, también el color visual. - `color` (jerárquico) — opcional, solo `primary | secondary`. Override visual cuando NO hay carga evaluativa. Si pasas `color='primary'` con `intent='threat'`, el intent gana. ```svelte ``` ### 2.3. Mapeo desde convención heredada | Convención CSS | Token UIX | Nota | |---|---|---| | primary | `primary` | jerarquía, no intent | | secondary | `secondary` | jerarquía, no intent | | success | `affirm` o `fulfill` | affirm = confirmación suave, fulfill = objetivo cumplido | | warning | `risk` | problema corregible | | danger | `threat` o `loss` | threat = antes, loss = después | | info | `signal.announce + neutral` | info no es intent, es función de atención | ### 2.4. Tokens CSS por theme Cada theme define los roles; el inventario y los nombres de slot reales (`--color-{role}-{slot}`) viven en [`theming/reference.md`](../theming/reference.md) §4 y en el generador (`DEFAULT_COLOR_ROLE_SLOT_STEPS`, `src/uix/eidos/lib/render-css.ts`). El naming `--color-{role}-element` de la versión original de esta guía nunca llegó al código. El theme decide hue/sat/lightness por marca. El componente solo declara qué token leer. ### 2.5. CSS en recipes ```css [data-color='primary'] { ... } [data-color='secondary'] { ... } [data-color='neutral'] { ... } [data-color='affirm'] { ... } [data-color='fulfill'] { ... } [data-color='risk'] { ... } [data-color='threat'] { ... } [data-color='loss'] { ... } ``` Los selectores legacy (`info`, `success`, `warning`, `danger`) se eliminan. --- ## 3. Subset por componente No todo componente acepta los 8 valores. Cada componente declara su subset permitido. ### 3.1. Tabla de subsets | Componente | color | intent permitidos | |---|---|---| | Button (acción) | primary, secondary | neutral, affirm, fulfill, risk, threat, loss | | Button (nav) | primary, secondary | neutral | | Toggle / Switch | primary, secondary | neutral, affirm, risk, threat | | Checkbox / Radio | primary, secondary | neutral, affirm | | Input | — | neutral, risk | | Select / Combobox | primary, secondary | neutral, affirm | | Slider | primary | neutral, affirm | | Alert inline | — | risk | | Alert crítica | — | threat | | Toast / Snackbar | — | neutral, affirm, risk | | Toast con undo | — | loss | | Banner | — | neutral, risk | | Badge | — | neutral, risk | | Modal | — | neutral, threat, risk | | Spinner / Skeleton | — | no acepta intent | | Progress bar | — | no acepta intent | ### 3.2. Regla de subset Si un componente no admite `loss`, no aparece `[data-color='loss']` en su recipe CSS. El intent se valida en TypeScript: ```ts type ToggleIntent = Extract; ``` ### 3.3. Regla: el intent no nace del componente El intent no nace del componente. Pero el componente debe poder recibirlo para expresarlo. La diferencia con la convención heredada es el orden: - **Convención:** diseñador pinta botón de rojo → botón "es" danger → intent nace del color - **UIX:** evento es commit.delete + loss → componente recibe loss → aplica firma perceptiva ### 3.4. Regla: la estética no contradice la semántica La estética tiene libertad dentro del rango que la semántica permite. Un affirm puede ser verde esmeralda o azul suave — eso es estética. Pero un affirm no puede ser rojo con icono de alerta — eso es contradicción semántica. --- ## 4. Familias y componentes: quién expresa qué ### 4.1. emerge vs shift > **Tabla orientativa (doctrina del libro), el morfo manda.** La > implementación asignó `emerge` a los overlays Dialog / Drawer / Popover: > sus morfos declaran `open`/`close` con `family: 'emerge'`, y el `close` > polimórfico admite `allowedFamilies: ['emerge', 'commit', 'signal']` — sin > `shift` (ver `src/uix/morfo/components/dialog.ts` y > [`book-deviations.md`](./book-deviations.md) D.11). Ante > cualquier duda, la familia real de un componente es la de su morfo. | Componente | Familia | Razón | |---|---|---| | Dropdown | emerge.open | aparición local, no cambia marco | | Popover | emerge.open | aparición anclada | | Tooltip | emerge.present | información auxiliar | | Accordion | emerge.expand | contenido contenido | | Modal / Dialog | emerge.open (implementado) | el libro lo doctrina shift.enter-mode; el morfo declara emerge | | Command Palette | shift.enter-mode | cambia régimen operativo | | Edit mode | shift.enter-mode | cambia qué puede hacerse | | Wizard step | shift.step | avanza en proceso | | Route change | shift.navigate | nuevo contexto | | Drawer (pesado) | shift.enter-mode | si bloquea fondo y captura foco | | Drawer (ligero) | emerge.open | si no bloquea ni captura | ### 4.2. contact vs commit ``` contact = el sistema recibió mi gesto commit = algo quedó fijado como consecuencia ``` Si la operación es instantánea (toggle, checkbox, select), el usuario percibe un solo evento: commit. El contact está implícito en el gesto. Se documenta como un solo evento semántico. ### 4.3. signal vs emerge ``` emerge = algo entra o sale del campo perceptivo signal = algo reclama atención ``` Un toast que aparece es emerge.present. El mensaje dentro puede ser signal.notify + neutral o commit.save + affirm. La aparición no es la señal. --- ## 5. Morfo: declaración semántica del componente ### 5.1. Estructura del evento en Morfo ```ts events: [ { name: 'commit-toggle', semantic: { family: 'commit', verb: 'toggle', intent: undefined, // lo decide el provider según contexto target: v.partRef('root'), sequence: 'post' // la señal ocurre después del cambio de estado } } ] ``` ### 5.2. Campo `sequence` (timing del evento) ```ts sequence: 'pre' | 'coincident' | 'post' ``` | Valor | Cuándo usar | Ejemplo | |---|---|---| | `pre` | señal perceptiva antes del cambio estructural | emerge.dismiss (animar salida antes de cerrar) | | `coincident` | señal durante el proceso | sustain.progress | | `post` | señal después del resultado real | commit.save + affirm (confirmar después de guardar) | No todo evento debe ser `pre` como el Toast dismiss. Contact debe ser `post` (inmediato, sin bloquear estado). Commit.save debe ser `post` (no celebrar antes de que exista resultado). ### 5.3. Capacidad semántica vs evento fijo Morfo puede declarar capacidad semántica cuando el componente soporta varias familias según contexto. El shape implementado es **aditivo** — el evento declara su semántica concreta como default y `allowedFamilies` habilita el override (el shape `defaultSemantic` de la versión original de esta guía se descartó; ver [`book-deviations.md`](./book-deviations.md) D.11). Del morfo real del Dialog: ```ts events: [ { name: 'close', semantic: { family: 'emerge', // default verb: 'close', target: v.partRef('content'), sequence: 'pre', persistence: 'transient', allowedFamilies: ['emerge', 'commit', 'signal'] } } ] ``` El provider concreta con un override validado contra `allowedFamilies`: ```ts runtime.trigger('close', { semantic: { family: 'commit', verb: 'save', intent: 'fulfill' } }); ``` --- ## 6. Sema: holds y persistencia ### 6.1. Separar hold expresivo de persistencia semántica ```ts hold: number // duración expresiva mínima del evento (ms) persistence: 'transient' | 'untilAction' | 'untilFix' | 'stateBound' ``` | Tipo | Significado | Ejemplo | |---|---|---| | `transient` | desaparece tras hold | contact.press, commit.save + affirm | | `untilAction` | persiste hasta que el usuario actúe | signal.alert + threat | | `untilFix` | persiste hasta corrección | signal.warn + risk | | `stateBound` | ligado al estado del proceso | sustain.progress | ### 6.2. Holds por familia e intent Los valores canónicos viven en el código — no se copian aquí (regla anti-drift de [`docs/authoring.md`](../authoring.md)): - **Holds base por familia**: `SEMA_MAP.families[F].hold` en `src/uix/sema/sema-map.ts`. - **Tabla de referencia holds-por-intent**: `SEMA_HOLDS_BY_INTENT` en `src/uix/sema/holds.ts` (referencia doctrinal, NO auto-aplicada — el default conservador es `transient` y cada morfo declara su `persistence`). La tabla numérica que ocupaba esta sección divergió del código en semanas; consulta siempre las dos fuentes de arriba. ### 6.3. Sustain no tiene hold fijo Sustain no es un evento transitorio. Es un estado que dura mientras dura el proceso. No se le asigna hold de 600ms ni de ningún valor fijo. Se gestiona como `stateBound`: el canal visual mantiene los atributos mientras el provider indique que el proceso sigue activo. --- ## 7. Canal visual: atributos DOM ### 7.1. Señales transitorias (Sema escribe, Eidos lee) ```html data-event="commit-toggle" data-event-id="sig-42" data-event-phase="active" data-event-family="commit" data-event-intent="affirm" ``` Viven durante el hold. Se limpian antes de resolver la Promise. ### 7.2. Estado persistente (Soma/Effects escriben, Eidos lee) ```html data-state="open" data-color="primary" data-intent="risk" data-disabled data-pressed ``` Viven mientras el estado sea verdadero. No son señales transitorias. ### 7.3. Regla: no mezclar transitorio y persistente El canal visual nunca toca atributos de estado (`data-state`, `data-intent`, `data-disabled`). El estado lo gestiona el runtime. Razón: estado es persistente y señal es transitoria. Pisar el mismo nombre fuerza al canal a borrar estado al limpiar. --- ## 8. Composiciones simultáneas ### 8.1. V1: un solo evento activo por target Mantener un solo `data-event` activo. Suficiente para: - dismiss, open, press, complete, toggle, select ### 8.2. V2 (futuro): slots semánticos Para composiciones simultáneas como: ``` shift.enter-mode + signal.alert + threat sustain.progress + signal.warn + risk handle.carry + signal.warn + threat ``` Añadir slots: ```html data-event-frame="shift.enter-mode" data-event-signal="signal.alert" data-event-intent="threat" ``` Así el modal (shift) no absorbe el intent de su contenido (signal). --- ## 9. Accesibilidad semántica en Morfo ### 9.1. Contrato a11y por evento ```ts a11ySemantic: { requiresPersistentTrace?: boolean; requiresLiveRegion?: boolean; requiresFocusMove?: boolean; keyboardEquivalent?: boolean; reducedMotionFallback?: 'state' | 'text' | 'focus' | 'none'; } ``` ### 9.2. Ejemplos ```ts // signal.warn + risk a11ySemantic: { requiresPersistentTrace: true, reducedMotionFallback: 'text' } // signal.alert + threat a11ySemantic: { requiresPersistentTrace: true, requiresLiveRegion: true, requiresFocusMove: true } // handle (drag) a11ySemantic: { keyboardEquivalent: true } // commit.delete + loss a11ySemantic: { requiresPersistentTrace: true } ``` --- ## 10. Sound y Haptic ### 10.1. Principios - Sound y Haptic reciben el signal directamente del engine, no leen DOM. - Son fire-and-forget: no bloquean al caller. - Son opcionales, proporcionales y nunca únicos. - La semántica debe poder vivir sin ellos. ### 10.2. Configuración por familia/intent ```ts sound: { enabled: false, // opt-in global allowFamilies: ['signal', 'commit'], // solo estas familias pueden sonar allowIntents: ['fulfill', 'threat', 'loss'], // solo estos intents muteFrequentEvents: true // silenciar contact frecuente } ``` ### 10.3. Eager-init AudioContext se crea/resume en el primer user gesture (click/touch/keydown en document, capture phase). Convención aplicable a cualquier canal con restricción de "primera vez en gesture": audio, vibration, fullscreen, clipboard write. --- ## 11. Firma perceptiva por evento Una firma perceptiva es el conjunto de decisiones de canal coordinadas alrededor de un evento. **Las dimensiones perceptivas no son un conjunto cerrado — pero ojo a quién las ejecuta.** A nivel del libro, una firma compone los canales de expresión (tiempo · motion · presencia · profundidad · forma · color · sonido · háptica). El framework los **REPARTE por dueño** y NO los ejecuta todos en sema: - **sema ejecuta 2 canales runtime** — `sound` + `haptic` (los únicos declarados en `SemaChannelSignatures`) — más el **meta-canal `visual`**, que no realiza ninguna modalidad: solo **estampa los `data-event-*` y temporiza el hold**. - **eidos materializa los canales visuales** (motion · presencia · profundidad · forma · color) reaccionando en CSS a esos `data-event-*` + `data-state` / `data-intent`. Eidos es el **único dueño de lo visual**. `SemaChannelSignatures` se extiende vía TypeScript declaration merging para añadir canales **de sema** con firma (`a11y`, `voice`, …) — NO para los visuales, que viven en eidos. Una rule de cascade afina `sound`/`haptic`; `motion`/`color` se ajustan en los recipes de eidos, no en la cascade. Los ejemplos de abajo enumeran las dimensiones **perceptivas** de cada evento — recuerda que motion/forma/color las realiza **eidos**, no sema. ### 11.1. Ejemplo: commit.save + affirm ``` tiempo: breve motion: asentamiento mínimo forma: marca persistente color: affirm (positivo discreto) sonido: no por defecto háptica: no por defecto accesibilidad: texto o estado visible ``` ### 11.2. Ejemplo: signal.alert + threat ``` presencia: dominante forma: bloque crítico color: threat (alta saliencia) texto: acción clara sonido: opcional, urgente motion: entrada saliente persistencia: hasta acción accesibilidad: foco + live region + persistente ``` ### 11.3. Ejemplo: commit.delete + loss ``` presencia: retirada + huella forma: undo si existe color: loss (grave/desaturado) sonido: seco/grave opcional motion: retirada/descenso persistencia: huella accesibilidad: anuncio + undo persistente + foco no perdido no usar: alarma sostenida de threat ``` --- ## 12. Arquetipos y familias frecuentes Relación orientativa entre archetipos de Morfo y familias: | Archetype | Familias frecuentes | |---|---| | trigger | contact, emerge, shift, commit | | content | emerge, shift, signal | | item | commit, handle, signal | | overlay | shift | | indicator | sustain, commit | | thumb | handle | | track | handle, sustain | No como regla rígida, sino como documentación que ayuda a decidir qué eventos puede expresar un componente. --- ## 13. Doctrina del API: soma compound, eidos option C > Actualizacion 2026-05-14: la forma flat con snippet slots queda retirada. > Eidos usa la option C disciplinada: root visual directo con hijos atados como > propiedades explicitas. La referencia operativa vive en > `src/uix/eidos/components/README.md`. ### 13.1. Las dos capas exponen responsabilidades distintas ``` soma + morfo → composable universal — TODO caso (avanzado, raro, custom) eidos → visual opinionado del design system sobre la misma anatomia ``` **Soma siempre es compound** (`Component.Provider`, `Component.Trigger`, `Component.Content`, …) por simetría: el desarrollador aprende un patrón único, todas las partes son visibles, el contrato cross-part (IDs, ARIA refs) queda explícito. **Eidos no inventa una API flat paralela.** La capa visual conserva la anatomia declarada por Morfo y materializada por Soma, pero expone un root visual directo: - **Single-part** (Toggle, Switch): el `default` es el componente visual completo. - **Multi-part** (Collapsible, Dialog, Drawer, Popover, Toast): el `default` es el root visual (``, ``, etc.) y los hijos se acceden como propiedades attached (``, ``, etc.). - No hay `Provider` publico en Eidos; `Provider` sigue siendo nombre de Soma. ### 13.2. Forma publica vigente ```svelte Open Title Close ``` El `index.ts` de cada componente attached usa asignacion explicita, no `Object.assign`: ```ts const Drawer = DrawerRoot as DrawerNamespace; Drawer.Trigger = Trigger; Drawer.Portal = Portal; Drawer.Content = Content; export { Drawer }; export default Drawer; ``` ### 13.3. La virtud arquitectonica La capa visual ya no mantiene dos verdades. Si una app necesita una composicion que el wrapper visual no cubre, baja a `$soma/components/{x}` y compone la primitiva headless directamente. Eidos no bloquea esa salida; simplemente no mantiene un segundo API flat con snippets que duplique el compound. --- ## 14. Plan de migración > **Histórico — plan ya ejecutado.** Las cinco prioridades y la tabla de > componentes de esta sección se completaron durante los sprints 2026-05 > (vocabulario, tokens, holds/persistence, sequence, a11ySemantic, y la > migración eidos completa). Se conserva como registro del arco de la > migración; el estado real de cada componente lo da > `npm run component:audit`, no esta tabla. ### 14.1. Prioridad 1 — Vocabulario 1. Renombrar `alert` → `signal` en SEMA_FAMILIES 2. Añadir `shift` a SEMA_FAMILIES 3. Añadir `loss` a `INTENTS` 4. Mover verbs: select/toggle a commit, acknowledge a commit, edit a shift 5. Actualizar SEMA_VERBS con la tabla completa ### 14.2. Prioridad 2 — Tokens de color 1. Añadir tokens: `secondary`, `affirm`, `loss` 2. Renombrar: `success → fulfill`, `warning → risk`, `danger → threat` 3. Eliminar: `info` 4. Implementar regla de resolución intent ↔ color en providers ### 14.3. Prioridad 3 — Holds y persistencia 1. Separar `hold` expresivo de `persistence` semántica 2. Actualizar holds por familia/intent según tabla 3. Sustain como `stateBound`, no hold fijo ### 14.4. Prioridad 4 — Sequence timing 1. Añadir campo `sequence` a morfo events 2. Implementar `pre | coincident | post` en runtime.trigger ### 14.5. Prioridad 5 — a11ySemantic 1. Añadir contrato a11ySemantic a morfo events 2. Implementar reducedMotionFallback, liveRegion, focusMove ### 14.6. Componentes por migrar | Componente | CSS legacy | Wrapper | Subset color | |---|---|---|---| | toggle | — | ✅ piloto | primary, secondary, neutral, affirm, risk, threat | | switch | switch.css | ⏳ | primary, secondary, neutral, affirm, risk, threat | | dialog | dialog.css | ⏳ | neutral, risk, threat | | drawer | drawer.css | ⏳ | neutral | | popover | popover.css | ⏳ | neutral | | toast | toast.css | ⏳ | neutral, affirm, risk, loss | | checkbox | checkbox.css | ⏳ | primary, secondary, neutral, affirm | | accordion | accordion.css | ⏳ | neutral | | tabs | tabs.css | ⏳ | neutral | | tooltip | tooltip.css | ⏳ | neutral | --- ## 15. Reglas invariantes 1. **El intent no nace del componente.** Pero el componente debe poder recibirlo. 2. **La estética no contradice la semántica.** Libertad dentro del rango que el intent permite. 3. **Color expresa intent, no lo define.** El intent viene de la evaluación del evento. 4. **Emerge ≠ shift.** Dropdown = emerge. Modal = shift. 5. **Threat ≠ loss.** Antes de la consecuencia ≠ después de la consecuencia. 6. **Affirm ≠ fulfill.** Confirmación suave ≠ objetivo cumplido. 7. **Info no es intent.** Es signal.announce + neutral. 8. **Sustain no tiene hold fijo.** Dura mientras dure el proceso. 9. **Los canales no-visuales no leen DOM.** Reciben signal del engine. 10. **La semántica debe sobrevivir sin color, sin motion, sin sonido y sin háptica.** --- *Documento derivado de "Semántica perceptiva de la interfaz" (Navarro Leal) y la arquitectura UIX (Morfo/Soma/Sema/Eidos).*