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.
635 lines
18 KiB
635 lines
18 KiB
|
5 months ago
|
# Guía de implementación — Semántica perceptiva en UIX
|
||
|
|
|
||
|
|
## 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.
|
||
|
|
|
||
|
|
Documento autoritativo para toda migración, wrapper nuevo o extensión de componente.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. Vocabulario canónico corregido
|
||
|
|
|
||
|
|
### 1.1. Familias
|
||
|
|
|
||
|
|
7 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?
|
||
|
|
] 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.
|
||
|
|
|
||
|
|
```ts
|
||
|
|
export const SEMA_INTENTS = [
|
||
|
|
'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
|
||
|
|
] as const;
|
||
|
|
```
|
||
|
|
|
||
|
|
### 1.3. Verbs por familia
|
||
|
|
|
||
|
|
```ts
|
||
|
|
export const SEMA_VERBS = {
|
||
|
|
contact: [
|
||
|
|
'press', 'tap', 'activate', 'focus', 'trigger', 'release'
|
||
|
|
],
|
||
|
|
commit: [
|
||
|
|
'select', 'toggle', 'save', 'submit', 'confirm', 'cancel',
|
||
|
|
'complete', 'fail', 'delete', 'restore', 'reset', 'discard',
|
||
|
|
'expire', 'acknowledge', 'set', 'remove', 'reorder'
|
||
|
|
],
|
||
|
|
signal: [
|
||
|
|
'announce', 'notify', 'warn', 'alert', 'emphasize', 'remind'
|
||
|
|
],
|
||
|
|
handle: [
|
||
|
|
'pick', 'carry', 'drop', 'drag', 'resize', 'reorder', 'rotate', 'scroll'
|
||
|
|
],
|
||
|
|
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', 'end'
|
||
|
|
]
|
||
|
|
} 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
|
||
|
|
|
||
|
|
### 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
|
||
|
|
<Toggle /> <!-- data-color='neutral' -->
|
||
|
|
<Toggle color="primary" /> <!-- data-color='primary' -->
|
||
|
|
<Toggle intent="affirm" /> <!-- data-color='affirm' -->
|
||
|
|
<Toggle intent="threat" color="primary" /> <!-- data-color='threat' (intent gana) -->
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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 8 tokens:
|
||
|
|
|
||
|
|
```css
|
||
|
|
:root {
|
||
|
|
--color-primary-element: ...;
|
||
|
|
--color-secondary-element: ...;
|
||
|
|
--color-neutral-element: ...;
|
||
|
|
--color-affirm-element: ...;
|
||
|
|
--color-fulfill-element: ...;
|
||
|
|
--color-risk-element: ...;
|
||
|
|
--color-threat-element: ...;
|
||
|
|
--color-loss-element: ...;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
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<SemanticIntent, 'neutral' | 'affirm' | 'risk' | 'threat'>;
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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
|
||
|
|
|
||
|
|
| 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 | shift.enter-mode | cambia marco, captura foco, subordina fondo |
|
||
|
|
| 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 varios eventos según contexto:
|
||
|
|
|
||
|
|
```ts
|
||
|
|
events: [
|
||
|
|
{
|
||
|
|
name: 'close',
|
||
|
|
semantic: {
|
||
|
|
allowedFamilies: ['shift', 'commit', 'emerge'],
|
||
|
|
defaultSemantic: { family: 'shift', verb: 'exit-mode' }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
]
|
||
|
|
```
|
||
|
|
|
||
|
|
El provider concreta:
|
||
|
|
|
||
|
|
```ts
|
||
|
|
// Dialog con cambios sin guardar
|
||
|
|
semantic: { family: 'commit', verb: 'discard', intent: 'loss' }
|
||
|
|
|
||
|
|
// Dialog informativo
|
||
|
|
semantic: { family: 'shift', verb: 'exit-mode' }
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 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
|
||
|
|
|
||
|
|
```ts
|
||
|
|
const SEMA_HOLDS = {
|
||
|
|
contact: 120,
|
||
|
|
emerge: 180,
|
||
|
|
shift: 240,
|
||
|
|
commit: {
|
||
|
|
neutral: 200,
|
||
|
|
affirm: 180,
|
||
|
|
fulfill: 280,
|
||
|
|
risk: 240,
|
||
|
|
threat: 240,
|
||
|
|
loss: 240
|
||
|
|
},
|
||
|
|
signal: {
|
||
|
|
neutral: 240,
|
||
|
|
risk: 'untilFix',
|
||
|
|
threat: 'untilAction',
|
||
|
|
loss: 400
|
||
|
|
},
|
||
|
|
handle: {
|
||
|
|
pick: 120,
|
||
|
|
drop: 180
|
||
|
|
},
|
||
|
|
sustain: 'stateBound'
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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 Vibra
|
||
|
|
|
||
|
|
### 10.1. Principios
|
||
|
|
|
||
|
|
- Sound y Vibra 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.
|
||
|
|
|
||
|
|
### 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. Plan de migración
|
||
|
|
|
||
|
|
### 13.1. Prioridad 1 — Vocabulario
|
||
|
|
|
||
|
|
1. Renombrar `alert` → `signal` en SEMA_FAMILIES
|
||
|
|
2. Añadir `shift` a SEMA_FAMILIES
|
||
|
|
3. Añadir `loss` a SEMA_INTENTS
|
||
|
|
4. Mover verbs: select/toggle a commit, acknowledge a commit, edit a shift
|
||
|
|
5. Actualizar SEMA_VERBS con la tabla completa
|
||
|
|
|
||
|
|
### 13.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
|
||
|
|
|
||
|
|
### 13.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
|
||
|
|
|
||
|
|
### 13.4. Prioridad 4 — Sequence timing
|
||
|
|
|
||
|
|
1. Añadir campo `sequence` a morfo events
|
||
|
|
2. Implementar `pre | coincident | post` en runtime.trigger
|
||
|
|
|
||
|
|
### 13.5. Prioridad 5 — a11ySemantic
|
||
|
|
|
||
|
|
1. Añadir contrato a11ySemantic a morfo events
|
||
|
|
2. Implementar reducedMotionFallback, liveRegion, focusMove
|
||
|
|
|
||
|
|
### 13.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 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 14. 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).*
|