20 KiB
| title | type | audience | authority | status | sources | ||||
|---|---|---|---|---|---|---|---|---|---|
| UIX Semantic Canon | canon | human + agent | canonical — single source of truth for the semantic vocabulary | current |
|
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. 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 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
(SemaValencedFamily | SemaTransitionalFamily). Per-family base signature,
active channels and hold: SEMA_MAP.families in
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 — owned
by UIX itself, not by a layer. Per-intent perceptual deltas: SEMA_MAP.intents
in 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.infois 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):
- 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 ofcommit.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-checkaffirm /commit-toggle-uncheckneutral). Mode-switch semantics, where the weight depends on WHAT is being switched, use one event withintent: { 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 (SEMA_FAMILY_POLICY):
intentRequirement('required' | 'optional' | 'forbidden') — the compile-time gate.'required'(onlycommitandsignal) →intentis mandatory inMorfoEventSemantic.'forbidden'is reserved (no family uses it today). Drives theSemaEvent/MorfoEventSemanticdiscriminated 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 — including the documented
author extensions (e.g. commit.unselect, handle.zoom, signal.inform),
tracked in decisions/book-deviations.md.
The book's base verbs, by family:
contact(ch. 22): press · tap · activate · focus · trigger · releasecommit(ch. 23): select · toggle · save · submit · confirm · complete · fail · cancel · reset · discard · delete · restore · expire · acknowledgesignal(ch. 24): announce · notify · warn · alert · emphasize · remindhandle(ch. 25): pick · carry · drop · drag · resize · reorder · rotate · scrollemerge(ch. 26): present · dismiss · open · close · expand · collapse · reveal · hideshift(ch. 27): enter-mode · exit-mode · navigate · route · step · return · contextsustain(ch. 28): start · progress · loading · waiting · syncing · processing · streaming · pending · retrying · enddelegate(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 D.8):
- sema runtime channels =
sound+hapticonly (the two inSemaChannelSignatures), plus the projectedvisualmeta-channel that stampsdata-event-*during the hold.motion/color/presence/depth/shapewere 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; what a recording can and cannot carry (the intent only moves its LEVEL — physics, not policy) in 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 — 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 D.12.
Persistence (lifecycle) is separate — SignalPersistence in
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):
- contact precedes the result when there is direct action (agency).
- sustain precedes commit when there is a wait (continuity).
- threat precedes loss — never
commit.delete + threat(before/after the consequence). - emerge does not absorb the content's intent —
emerge.open → signal.warn + risk, notemerge.open + risk(frame ≠ message). - 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
shiftwas exactly that in this framework — it sounded (slide) and did not slide, becauseBUILTIN_SIGNATUREShad a FAMILY-keyed visual signature forcontact,commitanddelegateand none forshift(emergeandsignalhave theirs keyed by event name, so they were never mute). It has one now, and it is directional: seedata-event-directionin §9. - handle concentrates evaluation on the drop, not the carry.
- 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_VOCABULARYinsrc/uix/morfo/types.ts, emitted asdata-archetype. - The
data-event-*tokens — the contact surface sema stamps and eidos reads (see architecture/sema). 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 oneshift-navigateis the previous month and the next one is the following month. So the sense is decided per emit, likeintent, and likeintentit 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 §4–7 and
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(FINAL edition, 426 pp. — the canonical anchor, tracked in-repo; chapter map inbook-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.