--- title: UIX Semantic Canon type: canon audience: human + agent authority: canonical — single source of truth for the semantic vocabulary status: current sources: book: docs/Disenando_lo_que_ocurre_HOMOGENEIZADO.pdf ("Diseñando lo que ocurre", ed. homogeneizada 2026-07 — the earlier v2_3.md no longer exists) code: src/uix/sema/verbs.ts, src/uix/sema/types.ts, src/uix/intent.ts, src/uix/sema/sema-map.ts, src/uix/morfo/types.ts --- # UIX Semantic Canon This is the **single source of truth** for the framework's semantic vocabulary: the families, intents, verbs, the intent policy, the composition rules, and the expression channels. It exists so the rest of the corpus **stops re-transcribing the vocabulary** (which is how it silently drifted: "7 families" survived in three docs after the canon moved to 8). Every other doc links here. > **Need the concrete closed sets to build from** (every family's verbs and > default hold, the intents, haptic kinds, part archetypes, palette scales, > sizes, variants, shared strings)? They are spelled out — GENERATED from the > code, so they never drift — in [`canon/vocabularies.md`](./canon/vocabularies.md). > This file is the _doctrine_; that one is the _enumerated lists_. Two anchors hold this canon honest: - **The book** — `docs/Disenando_lo_que_ocurre_HOMOGENEIZADO.pdf` ("Diseñando lo que ocurre", edición homogeneizada). The editorial source. Each entry cites its chapter. - **The code** — the runtime is the executable form. Concrete _values_ (per-family holds, channel signatures, the full verb lists with author extensions) live in code and are **linked, not copied here**, on purpose. This doc fixes the _doctrine_; the code fixes the _numbers_. > Rule: if you need to state a family, an intent, a verb, or a composition rule > in any other doc, **link this file** — do not paste a copy. A second copy is a > future drift. --- ## 1. The canonical chain Every perceptible event resolves through one chain (book ch. 11, §0; ch. 30): ``` event → family → verb / phase → evaluation (if any) → intent → expression channels → unified reading ``` The framework encodes this chain literally: **morfo declares** it, **soma executes** it (`runtime.trigger`), **sema** carries the perceptual vocabulary and dispatches channels, **eidos** materializes the visual channels. See [architecture/active-architecture.md](./architecture/active-architecture.md) for the layer wiring; this file is only the _vocabulary_ that flows through it. --- ## 2. The 8 families A family is **not** a component or an effect. It is _a recurring class of event that answers one perceptual question_ (book ch. 8, ch. 21). The eight are the reduction of a wider exploratory set down to a minimal-yet-sufficient grammar (book ch. 8 §1–3). Canonical list: `SemaFamily` in [`src/uix/sema/types.ts`](../src/uix/sema/types.ts) (`SemaValencedFamily | SemaTransitionalFamily`). Per-family base signature, active channels and hold: `SEMA_MAP.families` in [`src/uix/sema/sema-map.ts`](../src/uix/sema/sema-map.ts). | Family | Perceptual question | Accepts intent | Book | | ---------- | -------------------------------------------- | ------------------- | ------ | | `contact` | ¿el sistema ha sentido mi acción? | leve / anticipatory | ch. 22 | | `commit` | ¿qué quedó fijado o tuvo consecuencia? | **fully** | ch. 23 | | `signal` | ¿algo reclama mi atención? | **fully** | ch. 24 | | `handle` | ¿estoy manipulando directamente este objeto? | only on `drop` | ch. 25 | | `emerge` | ¿algo entró o salió del campo perceptivo? | normally no | ch. 26 | | `shift` | ¿cambió el contexto / régimen? | normally no | ch. 27 | | `sustain` | ¿esto sigue ocurriendo? | normally no | ch. 28 | | `delegate` | ¿quién actúa ahora? | no by default | ch. 29 | `contact`/`commit`/`signal`/`handle` are the **valenced** families; `emerge`/`shift`/`sustain`/`delegate` are the **transitional** ones (book ch. 9). That split is a _classification_; it no longer dictates the intent rule — the policy (§4) does. The perceptual question is a **demand on the expression**, not a label: a family that fails to answer its own question has failed at the only thing it exists for. `shift` is the case that proves it — its question IS the crossing, so a `shift` nobody perceives crossing is the _shift invisible_ of the antipattern catalogue (ch. 34 §14), and it was exactly that here until 2026-08-11: it sounded like a slide and did not slide (§8 rule 5; the sense it now travels in, `data-event-direction`, in §9). --- ## 3. The 6 intents An intent is the **evaluative modulation** of an evaluable event — a region of the valence/activation space (Russell), **not** a color (book ch. 9 §6, ch. 10). Canonical list: `INTENTS` in [`src/uix/intent.ts`](../src/uix/intent.ts) — owned by UIX itself, not by a layer. Per-intent perceptual deltas: `SEMA_MAP.intents` in [`sema-map.ts`](../src/uix/sema/sema-map.ts). | Intent | Valence / activation | Reading | Book | | --------- | ------------------------------------- | -------------------------------------- | ------ | | `neutral` | low activation, no strong valence | notar sin tono | ch. 10 | | `affirm` | positive, low activation | "todo va bien" (confirmación suave) | ch. 10 | | `fulfill` | positive, mid-high activation | "objetivo cumplido" (resuelve tensión) | ch. 10 | | `risk` | negative, moderate activation | "revisa esto" (corregible) | ch. 10 | | `threat` | negative, high activation | "atiende ahora" (aún evitable) | ch. 10 | | `loss` | negative, lower activation, posterior | "ya ocurrió" (consecuencia consumada) | ch. 10 | Three distinctions the inherited semaphore (`success`/`warning`/`danger`/`info`) collapses, and that this canon keeps apart (book ch. 10 §4): - **`threat` ≠ `loss`** — threat convokes action _before_ the consequence; loss registers it _after_. They must not look/sound the same. - **`affirm` ≠ `fulfill`** — soft confirmation vs resolution of a tension. - **`info` is not an intent** — it is attentional salience → `signal.* + neutral`. ### Intent assignment — undo matrix and binary toggles (framework materialization, 2026-07-07) Two assignment rules the catalog already practiced consistently. THIS section is the canonical registry; they were canonized at the component-audit checkpoint (verdicts C1/C3 — historical record in [`audit/components/_veredictos.md`](./audit/components/_veredictos.md)): - **The undo matrix.** Removing an item from a collection (`commit.unselect`) = **`affirm`** — an active choice, as affirmative as selecting it (calendar, select, combobox, grid-list, listbox). Un-marking a binary (the uncheck direction of `commit.toggle`) = **`neutral`**. Abandoning or clearing (`commit.reset`, `commit.cancel`) = **`neutral`**. - **Binary toggles — two sanctioned models.** _Mark-done_ semantics, where the direction itself carries the evaluative load, use two static directional events (checkbox: `commit-toggle-check` affirm / `commit-toggle-uncheck` neutral). _Mode-switch_ semantics, where the weight depends on WHAT is being switched, use one event with `intent: { fromProp: 'intent', default: 'neutral' }` so the consumer grades it (switch, toggle, toggle-group). Choose by the question: does the ON direction intrinsically mean progress/agreement? → static directional. Does context decide? → `fromProp`. --- ## 4. Intent policy — `SEMA_FAMILY_POLICY` Whether intent is _required_ is set **per family by policy**, not by the valenced/transitional split (book ch. 8 §6, ch. 9). Two independent axes, in [`src/uix/sema/types.ts`](../src/uix/sema/types.ts) (`SEMA_FAMILY_POLICY`): - **`intentRequirement`** (`'required' | 'optional' | 'forbidden'`) — the compile-time gate. `'required'` (only `commit` and `signal`) → `intent` is mandatory in `MorfoEventSemantic`. `'forbidden'` is reserved (no family uses it today). Drives the `SemaEvent` / `MorfoEventSemantic` discriminated unions. - **`intentGuidance`** (`'expected' | 'contextual' | 'discouraged'`) — doctrinal hint, no type effect. `commit`/`signal` = `expected`; `contact` = `discouraged` (book ch. 22 §11: _"el intent fuerte no debería vivir en el contacto"_); the rest = `contextual`. Runtime: `validateSemaEvent` throws when a `required` family is built without intent. --- ## 5. The evaluable-vs-structural rule > **The intent lives in the evaluable event (the message), not in the structural > container (the frame).** (book ch. 9 — _"Qué eventos son mensaje y qué eventos > son marco"_; restated in Appendix A.) A modal that opens is `shift.enter-mode` (frame); the warning inside it is `signal.warn + threat` (message). A spinner is `sustain.progress` (frame); the failure after it is `commit.fail + risk` (message). Cargar el intent en el marco es el antipatrón capital (book ch. 34 §5 "Modal rojo", §4 "todo lo que aparece es alerta"). In the framework this is enforced declaratively: the event that _carries_ the intent declares it in its morfo `events[].semantic.intent`; the frame event does not. The runtime never infers intent (book **Appendix A**: _"El intent no se hereda implícitamente… El runtime no debe adivinar. La semántica debe estar declarada."_). This is exactly the morfo → soma → sema → eidos contract. --- ## 6. Canonical verbs — `SEMA_VERBS` Verbs concrete the action within a family (book ch. 8 §"niveles", ch. 22–29). `morfo.events[].semantic.verb` MUST be in its family's set; `events[].name` **declares the family** — `{family}-{verb}[-{nuance}]`, guaranteed by `validateMorfo` — so sema / sound / haptic / eidos can subscribe transversally. **Executable source of truth:** `SEMA_VERBS` in [`src/uix/sema/verbs.ts`](../src/uix/sema/verbs.ts) — including the documented author extensions (e.g. `commit.unselect`, `handle.zoom`, `signal.inform`), tracked in [decisions/book-deviations.md](./decisions/book-deviations.md). The book's base verbs, by family: - `contact` (ch. 22): press · tap · activate · focus · trigger · release - `commit` (ch. 23): select · toggle · save · submit · confirm · complete · fail · cancel · reset · discard · delete · restore · expire · acknowledge - `signal` (ch. 24): announce · notify · warn · alert · emphasize · remind - `handle` (ch. 25): pick · carry · drop · drag · resize · reorder · rotate · scroll - `emerge` (ch. 26): present · dismiss · open · close · expand · collapse · reveal · hide - `shift` (ch. 27): enter-mode · exit-mode · navigate · route · step · return · context - `sustain` (ch. 28): start · progress · loading · waiting · syncing · processing · streaming · pending · retrying · end - `delegate` (ch. 29): offer · plan · authorize · act · review · escalate · return Verbs that look like one family but belong to another (book canon): `select` / `toggle` / `acknowledge` are `commit` (they fix state); `edit` is expressed as `shift.enter-mode` (régimen change), not a `handle` verb. --- ## 7. Expression channels + the owner split The book defines **8 expression channels** (book ch. 11–20): tiempo · motion · presencia · profundidad · forma · color · sonido · háptica. _"El color expresa el intent, no lo define"_ (ch. 16); _"ningún canal agota la semántica"_ — robustness = a reading that survives the loss of a channel (ch. 19, ch. 33). The framework **splits these channels by owner** (decision recorded in [decisions/book-deviations.md](./decisions/book-deviations.md) D.8): - **sema runtime channels** = `sound` + `haptic` only (the two in `SemaChannelSignatures`), plus the projected `visual` meta-channel that stamps `data-event-*` during the hold. `motion`/`color`/`presence`/`depth`/`shape` were removed from sema. - **eidos** materializes the visual channels (motion / presence / depth / shape / color) by reacting in CSS to `data-event-*` + the morfo's state attrs. **How each channel is AUTHORED** — the owner split says who materializes a channel; this says what a component actually WRITES, which is the part authors get wrong: | Channel | The component writes | Where the values live | | -------------------------------------------------- | ---------------------------------------------------------------------- | -------------------------------------- | | visual (motion / color / presence / depth / shape) | nothing — it declares the EVENT; eidos reacts in CSS to `data-event-*` | eidos recipes + the token engines | | `sound` | **one NAME**, and only when it differs from its family's default | `SEMA_MAP.sounds` — the sound pack | | `haptic` | a categorical `kind` (`tick` / `tap` / `pulse` / `success` / …) | `HapticChannel` maps kinds to patterns | The rule is the same in all three rows and it is the point: **a component says WHAT occurs, never how loud, how bright or how long.** **And the intent SELECTS; it does not modulate.** Resolution is two lookups — `pack[name.intent]`, then `pack[name]`, then silence. A `threat` is not a `tick` with more roughness: it is `tick.threat`, another sound, designed whole. Where a pack ships no variant for an intent, the intent is simply ignored and the base sounds. That is deliberate: we recognise SOUNDS, not displacements of a parameter, and a system that bends one tone produces variants that differ on paper and are the same to the ear. The name itself usually comes from the map, not from the component: `SEMA_MAP.families[f].sounds` is keyed by the event's canonical VERB, so an `emerge` + `close` finds `close` with nothing written anywhere. A component writes a name only when it DIFFERS. Adding a sound means **adding a NAME to the pack**, never widening a rule. Detail in [architecture/sema.md](./architecture/sema.md); what a recording can and cannot carry (the intent only moves its LEVEL — physics, not policy) in [decisions/book-deviations.md](./decisions/book-deviations.md) D.7, retired but kept for that measurement. So the book's 8 channels are conceptual; at runtime they are owned by different layers. `hold` (the registration FLOOR — a minimum, never a ceiling: the expression completes before unstamping, capped only by the channel's engineering guard) resolves per family + intent from `SEMA_HOLDS_BY_INTENT` ([`holds.ts`](../src/uix/sema/holds.ts) — the single hold source since 2026-07-06; the duplicated per-family `SEMA_MAP` hold was removed) over the `SEMA_DURATIONS` scale (glimpse / brief / settled / noticed / …). The ms are the framework's MATERIALIZATION of the book's qualitative regions (cap. 32, TABLA 32.1/32.2 — the book deliberately gives no numbers); see [decisions/book-deviations.md](./decisions/book-deviations.md) D.12. Persistence (lifecycle) is separate — `SignalPersistence` in [`types.ts`](../src/uix/sema/types.ts) (book ch. 12 §6, ch. 4 §13: la huella se declara). --- ## 8. Composition rules Real interaction is a temporal composition of events, not one family (book ch. 30, ch. 6 §13). The rules are derived from perceptual need, not decree (ch. 30 §3): 1. **contact precedes the result** when there is direct action (agency). 2. **sustain precedes commit** when there is a wait (continuity). 3. **threat precedes loss** — never `commit.delete + threat` (before/after the consequence). 4. **emerge does not absorb the content's intent** — `emerge.open → signal.warn + risk`, not `emerge.open + risk` (frame ≠ message). 5. **shift must orient** the context change (focus + title + landmark) — and it must be PERCEIVED crossing it. A frame that swaps with nothing moving is the _shift invisible_ of the antipattern catalogue (ch. 34 §14): the user is somewhere else and was never told they travelled. Until 2026-08-11 `shift` was exactly that in this framework — it sounded (`slide`) and did not slide, because `BUILTIN_SIGNATURES` had a FAMILY-keyed visual signature for `contact`, `commit` and `delegate` and none for `shift` (`emerge` and `signal` have theirs keyed by event name, so they were never mute). It has one now, and it is directional: see `data-event-direction` in §9. 6. **handle concentrates evaluation on the drop**, not the carry. 7. **every open process needs an exit** (commit / cancel / fail / persisted-warn). On simultaneity, attentional dominance (ch. 30 §7): the evaluable event dominates the structural; higher activation dominates lower; on a tie, the most recent. --- ## 9. Naming + the declarative layer Name **events, not effects** (book ch. 36): `contact.press`, `commit.save + affirm`, `signal.warn + risk` — never `redAlert`, `successToast`, `shakeError`. The grammar is implemented as a **declarative layer above components, tokens and patterns** (book Appendix A) — in UIX, the **morfo** map: ``` morfo declares → soma executes (runtime.trigger) → sema vocabulary + channels → eidos materializes (reads DOM) ``` Cross-layer vocabularies that anchor this: - **Archetypes** — cross-component part classification, `ARCHETYPE_VOCABULARY` in [`src/uix/morfo/types.ts`](../src/uix/morfo/types.ts), emitted as `data-archetype`. - **The `data-event-*` tokens** — the contact surface sema stamps and eidos reads (see [architecture/sema](./architecture/sema.md)). Six: `data-event` · `-family` · `-intent` · `-direction` · `-phase` · `-id`. - **Sense of traversal** — `data-event-direction`, `'forward' | 'backward'` (`SemaDirection`). Two values because the event NAME already carries every distinction a DECLARATION can make (`shift-enter-mode` ≠ `shift-exit-mode`); what a name cannot carry is which way THIS occurrence went, since one `shift-navigate` is the previous month and the next one is the following month. So the sense is decided per emit, like `intent`, and like `intent` it is optional — a route with no sense of its own (a month picked from a select) stamps none, because an invented sense is worse than none. It is a SENSE, not an axis: eidos maps it onto the inline axis, so `:dir(rtl)` flips it and no layer above CSS knows about left or right. --- ## 10. Visual canon (sibling) This file is the **semantic** canon. The **visual** canon — the Token Scope Contract (`--{component}-*` public / `--_{component}-*` internal; never `--eidos-*`/`--air-*`/`--terra-*`/`--soma-*`), the 9 color roles, the size scale, and the variant vocabulary (`EIDOS_VARIANTS`) — lives in [eidos/THEMING.md](./theming/reference.md) §4–7 and [eidos/lib/types.ts](../src/uix/eidos/lib/types.ts). Variants are eidos canon, not theme (THEMING §19): a theme re-tints what is perceptually fixed; it does not invent families, intents or variants. --- ## Sources - **Book** — [`docs/Disenando_lo_que_ocurre_FINAL.pdf`](./Disenando_lo_que_ocurre_FINAL.pdf) (FINAL edition, 426 pp. — the canonical anchor, tracked in-repo; chapter map in [`book-map.md`](./book-map.md)): families ch. 8 + 21–29 · intents ch. 9–10 · channels ch. 11–20 · composition ch. 30 · matrices ch. 31 · ranges ch. 32 · accessibility ch. 19 + 33 · validation ch. 35 · theory→system ch. 36 · declarative layer Appendix A. - **Code** — `verbs.ts` (`SEMA_VERBS`) · `types.ts` (`SemaFamily`, `SEMA_FAMILY_POLICY`, `SignalPersistence`) · `intent.ts` (`INTENTS`) · `sema-map.ts` (`SEMA_MAP`) · `morfo/types.ts` (`ARCHETYPE_VOCABULARY`). - **Deviations from the book** — [`decisions/book-deviations.md`](./decisions/book-deviations.md).