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.
svelte-kit-vice/docs/canon/recipe-contract.md

186 lines
12 KiB

---
title: Recipe Contract — the transversal contract of every eidos recipe
type: canon
audience: human + agent
authority: canonical — which theming systems every recipe MUST consume, and under which names
status: current
enforcement: scripts/component-audit.ts (rules R-4.x, all `error`)
source: migrated from src/uix/eidos/RECIPE_CONTRACT.md (2026-07-02, docs-book F7.3)
related:
tsc: docs/canon/tsc.md (where each token is emitted)
theming: docs/theming/reference.md (the system; §5 sizes · §6 naming)
guide: docs/theming/guide.md (how to add a component, step by step)
---
# Recipe Contract
> The TSC decides **where** each token is emitted. This contract decides
> **what** every recipe consumes: the theming's transversal systems
> (state-layer, focus, elevation, typography, opacity, motion, spacing,
> touch, lists, z) and the vocabulary of its dimensional tokens. A recipe
> deviating without an annotated exception is out of contract — the
> `component-audit` (R-4.x) flags it.
>
> **Why it exists**: the 2026-07-01 audit (`fable-eidos-audit.md`) measured
> that canons defended by a mechanical guard drift ~0% while canons defended
> by prose drift 30–85% (state-layer 21/135, partial depth-channel adoption,
> 49 keyframes outside the channel). This document is the canon; the R-4.x
> rules are its executable form.
---
## 1. A recipe's skeleton — dimensional tokens
Every recipe declares its knobs in `lib/recipes/base.ts` with this
vocabulary. **The names are canon**, just like the color slots are in
THEMING §6:
| Concept | Canonical name | Forbidden |
| --- | --- | --- |
| Control height per size | `control-height-{size}` (or `{part}-height-{size}` when the part is not the control) | inventing a third name per component |
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
| Padding per axis | `padding-inline[-{size}]` / `padding-block[-{size}]` | **`padding-x` / `padding-y`** AND the abbreviated segments **`px` / `py`** (physical axes) |
| Margin per axis | `margin-inline[-{size}]` / `margin-block[-{size}]` | `margin-x/-y`, `mx` / `my` |
| Internal separation | `gap[-{size}]` | — |
| Radius | `radius[-{size}]` → `var(--radius-*)` | px that equal a step of the scale |
| Control font | `font-size-{size}` = `var(--font-size-{size})` (**1:1**, THEMING §5) | literal px/rem (guarded in `recipe-css-contract`) |
| Icon | `icon-size-{size}` = `var(--icon-size-{size})` | hand px; size from `--control-height-*` unless the element IS a control |
| Field label | one typographic step below the input (THEMING §5) | — |
Inherited naming rules (THEMING §6): public `--{c}-{slot}`, private
`--_{c}-{slot}`, no `color-` segment, no kebab abbreviation, no
`--eidos-/-soma-/-air-`.
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
**Value-reference rules (2026-07-07, theming audit B — both guarded in
`recipe-css-contract.test.ts`):**
- A recipe token VALUE may reference only vocabulary that **exists** in the
emitted contract — a no-fallback `var(--x)` pointing at a name the
generators never emit is a *phantom* (it freezes the token
guaranteed-invalid; `--color-content-tertiary` shipped a month that way).
`var(--x, fallback)` is runtime-optional by construction.
- A value referencing a component **private** (`--_{c}-*`, declared in the
component's CSS) must declare a **scope that covers it** (`host` / leaf) —
never `root`: a `:root`-emitted token computes where the private does not
exist and freezes invalid down the whole tree (the calendar holiday/event
marks and the field segment heights shipped broken exactly this way).
> **Normalización ejecutada (2026-07-06, decisión usuario: "un idioma, todo
> el ecosistema").** El catálogo hablaba dos idiomas: 32 claves canónicas
> conviviendo con **198 claves abreviadas** `p[xy]` (+3 `my`) que evadían el
> regex de R-4.4 — hábito Tailwind/Chakra sin decisión que lo respaldara. El
> nombre canónico es el de la PLATAFORMA (CSS Logical Properties): el token
> se llama como la propiedad que alimenta (nombre = contrato, sin capa de
> traducción — a diferencia de Panda, donde `px` es azúcar que un compilador
> resuelve), es direccional-agnóstico, y en un contrato de tokens `px` además
> es un pun con la unidad. Codemod atómico value-preserving: 198+3 claves +
> ~570 nombres en **57 ficheros** (tracks excluidos words/palabras/chronos
> INCLUIDOS por decisión explícita) + los **20 shorthands físicos**
> (`padding: var(--py) var(--px)`) reescritos a longhands lógicos (el único
> subconjunto con semántica física real — modos de escritura verticales).
> Triple muro: R-4.4 con el regex ampliado a segmentos abreviados (error,
> sin allowlist — la deuda murió en el mismo pass) · **muro de tipos**
> `defineRecipes` (`lib/recipes/define.ts`): una clave de eje físico no
> compila · la suite/regen verifican (diff de generated = 230 renombres 1:1;
> computed padding idéntico en navegador).
## 2. Mandatory transversal systems
The table IS the contract: **concept → canonical mechanism → the rule that
guards it**. A recipe reimplements none of these concepts on its own.
| Concept | Canonical mechanism | How it is consumed | Rule |
| --- | --- | --- | --- |
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
| **Neutral hover / press / selected** | the state-layer (rules in `archetypes.css`; magnitudes are config data `primitives.state` — changelog §40) | transparent background → `background: var(--state-hover)`; filled background → the overlay `background-image: linear-gradient(var(--state-hover), var(--state-hover))` | R-4.3 |
| **Valenced hover** (solid / soft per color) | the recipe's palette swap | `background: var(--{c}-solid-hover)` etc. — tokens, never hand-rolled `color-mix(currentColor …)` | R-4.3 |
| **Elevation** | the scale + the depth channel | `box-shadow: var(--shadow-*)` or compose `var(--depth-{plane}-shadow), var(--depth-{plane}-halo)`; never literal shadows | R-4.1 |
| **Crisp inner ring** | the inset-ring (THEMING §29) | `box-shadow: inset 0 0 0 var(--ring-inset-width) var(--ring-inset-color, …)` — the expression lives at the point of use | R-4.1 (passes: it carries `var()`) |
| **Focus** | per archetype (THEMING §32) | fields → the two-ring `--focus-ring-*` (box-shadow); surfaces/controls → their own `outline` (HCM-safe); declaring nothing falls back to `archetypes.css` | R-1.5 |
| **Disabled** | the opacity token | `opacity: var(--opacity-disabled)` + `cursor: not-allowed` — never hand `0.4`/`0.5`/`0.6` | R-4.2 |
| **Typography** | 1:1 with the scale | see §1; canonical exceptions: avatar/marker (glyph ∝ diameter), accordion (prose scale) | R-2.7 + guard |
| **Motion — state transitions** | tokens | `transition: X var(--duration-*) var(--ease-*)` | THEMING §35 block C |
| **Motion — event signatures** | `EidosConfig.motion.{keyframes,signatures}` (generated, themeable) | NO local `@keyframes` for perceptual reactions; a local `@keyframes` only when **functional** (spin/shimmer/continuous period) and annotated | R-4.5 |
| **Spacing** | `var(--space-*)` | density × scaling arrive composed from the foundation — **never** multiply by `--density-*`/`--scaling` in the recipe | R-2.3 |
| **Touch target** | archetype (`trigger`/`close`/`action`) + list-surface | free via `@media (pointer: coarse)` in `archetypes.css` — don't declare your own 44px minimums | — |
| **List/menu rows** | the list-surface (`--list-item-*`) | list surfaces bridge `--list-item-height`/`-py`, not their own row heights | — |
| **Floating z-index** | the `--z-index-overlay-*` band | each portaled overlay consumes its rung; raw integers `0..5` only for intra-component order | guard in contracts.test |
| **Color** | role slots / recipe tokens | `var(--color-{role}-{slot})` or `var(--{c}-*)`; never direct `--scale-*`/`--primitive-*` nor hex/rgb | R-2.1/2.6, R-4.6 |
## 3. Exceptions — how they are declared
A deviation is valid **only** when annotated on the same line. The audit
honors annotations; an unannotated deviation is drift.
| Annotation | When | Example |
| --- | --- | --- |
| `/* literal: <reason> */` | a physically-fixed value or a justified optical tuning | `font-size: 13px; /* literal: tight icon affordance */` |
| `/* functional: <reason> */` | a local `@keyframes` that is NOT a perceptual signature (continuous period, machinery) | `/* functional: continuous spin period, not an event signature */` `@keyframes spin { … }` |
| physically-fixed | colors that must not follow the theme (the QR's white, the natural clock's skies) — hex allowed WITH a comment | `--_qr-bg: #ffffff; /* literal: QR quiet zone must be true white */` |
fix(cleanroom): F2+F3+F4-C+SEM-4s1 — lote mecánico, censos con guard, corpus documental y el close polimórfico de los pickers VIVO F2 — lote mecánico (13 ítems): - DEP-2 ogl eliminado (0 imports) · DEP-1 clsx inlineado como toClassString propio + suite de contrato (props.test.ts; soma.md §12 cerrado). - THM-7: los 5 selectores manuales de sema.md reescritos con semaSelector (los ejemplos [data-toast-root] apuntaban a un part INEXISTENTE — la deriva que el builder previene, demostrada en el propio doc). - MOR-1 escape isomorfo + validación de attr-names en semaSelector + 9 tests (selectors.test.ts, matches() real con comillas/corchetes) · MOR-2 partMarkerAttr = única fuente compilador↔builder + test de paridad · MOR-3 _resetCompileCache borrado (0 usos). - SOM-2 keydown continue en match sin handler + keyboardFixtureMorfo · SOM-1 no-await de handlers (censo async = 0; contrato V1 cumplido) + pin. - SEM-2 trigger pre-attacha catch con logger (void trigger sin unhandled rejection; throw intacto para awaiters) + pin · SEM-3 fallback muerto de applyDominance → skip defensivo + timer tope de awaitExpression cancelado · SEC-1 adjudicado YA implementado (assertCssVariableValue desde 2026-05-11) + pin del path de VALOR. - accordion → outline (§32; su outline:none dejaba CERO anillo en HCM) — verificado en vivo · THM-6 radius-full 9999px · EID-4 recuentos 33. F3 — censos con guard: - SOM-3 cerrado: announcer + image-provider migrados a scheduler-preferred (consumidores cableados: date/time-field vía soma.uix.timers; avatar/image vía eidos.timers — verificado en vivo); guard de timers ENSANCHADO de soma/components a TODO soma y pasado a EVIDENCIA (setTimeout exige .schedule( en el fichero — layers/ y datetime/ escapaban del ámbito viejo). - THM-5: R-4.7 nueva (válvula same-line /* important: <razón> */, escaneo comment-blanked) + las 15 declaraciones anotadas con su razón + canon recipe-contract §3/§4. - SOM-4 adjudicado: el censo/guard YA existían (49 pins); knob/mask-field/ timeline pinneados (overrides documentados en call-site); media-player Batch-4 (35 hits, cero renderProps) = único batch restante, registrado. - THM-4 doctrinado en eidos.md §unused (comportamiento/composición = legítimo; deuda = eje visual sin consumidor; hotspots por lotes). F4-C — corpus documental (decisiones de usuario aplicadas): - DOC-3: los 15 enlaces muertos resueltos (repoint a la edición FINAL trackeada / des-link históricos) · docs:check I6-links WARN→ERROR. - DOC-1: tabla «Build contract» MIGRADA a component-guide con estados modernizados (A3–A5 → LIVE + guards de hoy); banners reapuntados; citas de CANON/sema.md historificadas; lápida-redirect en el §13 del fósil. - DOC-4: hold chain → holds.ts · FAQ event:* SUPERSEDED por signatures · gradient añadido a los DOS capstones (sextet real) · nota de paleta de demo-authoring corregida (universalPaletteDecls + decisión THM-2 = mecanismo universal como sucesor del tracker borrado). - DOC-5/6: recuentos anti-frágiles datados · §4.11 dup → §4.12 · Known gaps historificado · N-6/N-7 recuperadas de git (d68d2c45^) y canonizadas en eidos.md §pickers · authoring E2 → canon/tsc.md · air-old des-linkado · EID-3 (placement) en la fila RTL · AUX-2 disabledDom documentado. SEM-4 sesión 1 — el close polimórfico de los pickers, VIVO (D.11): - Reconciliación: los morfos ya no declaran close (delegated al Popover, de-dialoged 06-27); el agujero real era el cierre programático bypaseando dismissWith → save/cancel/select eran perceptualmente SILENCIOSOS. - Fix: PickerShellHandle.setPopoverDismiss + closeWith(cause) en los 5 providers (14 sitios; select/commit → 'save' = commit.save+fulfill, cancel → 'cancel' = emerge; fallback raw para headless) + UN inyector en el eidos PickerShell root (norma N-8). Picker genérico fuera a propósito (ya suena commit-set/cancel por diseño S9). - Verificado en vivo (date-picker): Done → close·commit·fulfill·active · Cancel → close·emerge · cierre real. Gates: matriz 141/141 (los 6 morfos nuevos de la pista de texto paralela también PASS) · contracts 38/38 · eidos 314 · sema 178 · morfo 94 · docs:check 0/0 con I6 en error · baseline propio 57. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
| `/* important: <reason> */` | an `!important` that must win EVERY cascade fight — the three legitimate shapes: bypassing a soma inline style, cross-recipe fusion (the group's radius flattening), reduced-motion kills | `cursor: nwse-resize !important; /* important: wins over the header's inherited grab cursor */` |
What does **not** pass as an exception: "it's faster this way", "the token
didn't exist" (create it), "it's just a hover" (that is exactly the
contract's case).
## 4. Enforcement
| Rule | Guards | Severity |
| --- | --- | --- |
| R-4.1 | `box-shadow` without `var(` (elevation/ring outside tokens) | error |
| R-4.2 | literal `opacity: 0<N<1` outside `@keyframes` | error |
| R-4.3 | `background*` in `:hover` rules without a token (`var(`) or with manual `color-mix(… currentColor …)` | error |
| R-4.4 | recipe tokens with physical axes `padding-x/-y`, `margin-x/-y` | error |
| R-4.5 | a local `@keyframes` without a `/* functional: … */` annotation | error |
| R-4.6 | direct `var(--scale-*)` / `var(--primitive-*)` in component CSS | error |
fix(cleanroom): F2+F3+F4-C+SEM-4s1 — lote mecánico, censos con guard, corpus documental y el close polimórfico de los pickers VIVO F2 — lote mecánico (13 ítems): - DEP-2 ogl eliminado (0 imports) · DEP-1 clsx inlineado como toClassString propio + suite de contrato (props.test.ts; soma.md §12 cerrado). - THM-7: los 5 selectores manuales de sema.md reescritos con semaSelector (los ejemplos [data-toast-root] apuntaban a un part INEXISTENTE — la deriva que el builder previene, demostrada en el propio doc). - MOR-1 escape isomorfo + validación de attr-names en semaSelector + 9 tests (selectors.test.ts, matches() real con comillas/corchetes) · MOR-2 partMarkerAttr = única fuente compilador↔builder + test de paridad · MOR-3 _resetCompileCache borrado (0 usos). - SOM-2 keydown continue en match sin handler + keyboardFixtureMorfo · SOM-1 no-await de handlers (censo async = 0; contrato V1 cumplido) + pin. - SEM-2 trigger pre-attacha catch con logger (void trigger sin unhandled rejection; throw intacto para awaiters) + pin · SEM-3 fallback muerto de applyDominance → skip defensivo + timer tope de awaitExpression cancelado · SEC-1 adjudicado YA implementado (assertCssVariableValue desde 2026-05-11) + pin del path de VALOR. - accordion → outline (§32; su outline:none dejaba CERO anillo en HCM) — verificado en vivo · THM-6 radius-full 9999px · EID-4 recuentos 33. F3 — censos con guard: - SOM-3 cerrado: announcer + image-provider migrados a scheduler-preferred (consumidores cableados: date/time-field vía soma.uix.timers; avatar/image vía eidos.timers — verificado en vivo); guard de timers ENSANCHADO de soma/components a TODO soma y pasado a EVIDENCIA (setTimeout exige .schedule( en el fichero — layers/ y datetime/ escapaban del ámbito viejo). - THM-5: R-4.7 nueva (válvula same-line /* important: <razón> */, escaneo comment-blanked) + las 15 declaraciones anotadas con su razón + canon recipe-contract §3/§4. - SOM-4 adjudicado: el censo/guard YA existían (49 pins); knob/mask-field/ timeline pinneados (overrides documentados en call-site); media-player Batch-4 (35 hits, cero renderProps) = único batch restante, registrado. - THM-4 doctrinado en eidos.md §unused (comportamiento/composición = legítimo; deuda = eje visual sin consumidor; hotspots por lotes). F4-C — corpus documental (decisiones de usuario aplicadas): - DOC-3: los 15 enlaces muertos resueltos (repoint a la edición FINAL trackeada / des-link históricos) · docs:check I6-links WARN→ERROR. - DOC-1: tabla «Build contract» MIGRADA a component-guide con estados modernizados (A3–A5 → LIVE + guards de hoy); banners reapuntados; citas de CANON/sema.md historificadas; lápida-redirect en el §13 del fósil. - DOC-4: hold chain → holds.ts · FAQ event:* SUPERSEDED por signatures · gradient añadido a los DOS capstones (sextet real) · nota de paleta de demo-authoring corregida (universalPaletteDecls + decisión THM-2 = mecanismo universal como sucesor del tracker borrado). - DOC-5/6: recuentos anti-frágiles datados · §4.11 dup → §4.12 · Known gaps historificado · N-6/N-7 recuperadas de git (d68d2c45^) y canonizadas en eidos.md §pickers · authoring E2 → canon/tsc.md · air-old des-linkado · EID-3 (placement) en la fila RTL · AUX-2 disabledDom documentado. SEM-4 sesión 1 — el close polimórfico de los pickers, VIVO (D.11): - Reconciliación: los morfos ya no declaran close (delegated al Popover, de-dialoged 06-27); el agujero real era el cierre programático bypaseando dismissWith → save/cancel/select eran perceptualmente SILENCIOSOS. - Fix: PickerShellHandle.setPopoverDismiss + closeWith(cause) en los 5 providers (14 sitios; select/commit → 'save' = commit.save+fulfill, cancel → 'cancel' = emerge; fallback raw para headless) + UN inyector en el eidos PickerShell root (norma N-8). Picker genérico fuera a propósito (ya suena commit-set/cancel por diseño S9). - Verificado en vivo (date-picker): Done → close·commit·fulfill·active · Cancel → close·emerge · cierre real. Gates: matriz 141/141 (los 6 morfos nuevos de la pista de texto paralela también PASS) · contracts 38/38 · eidos 314 · sema 178 · morfo 94 · docs:check 0/0 con I6 en error · baseline propio 57. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
| R-4.7 | `!important` without a same-line `/* important: <reason> */` annotation (THM-5, 2026-07-11) | error |
**All R-4.x are `error`** (graduated 2026-07-02: the five mechanical ones
after the backfill; R-4.5 after the motion migration emptied its 15
components). The doctrine the migration left — **how a recipe consumes the
motion channel**:
1. **Preset stamp** (`data-animation-style` via the `motion` prop) when the
element has no semantic `data-state` of its own and no conditional
trigger: overlays (popover, menus, select, combobox, tooltip via the
`delayed-open` alias), content mounts (`motionAttrs`).
2. **Event signature** (`EidosConfig.motion.signatures`) when the animation
reacts to sema's `data-event-*` and is generic per verb/family
feat(morfo,sema,eidos): todo evento dice de que familia es, y el cruce por fin se ve El framework llamaba a la misma cosa de dos maneras: `open` pelado en ocho componentes y `emerge-open` en tres. No era estetica — un preset de movimiento engancha el nombre con `^=`, asi que el dialecto pelado no casaba con ninguna firma y simplemente no animaba, sin romper una sola prueba. Los 40 nombres sin prefijo pasan a `{familia}-{verbo}[-{matiz}]`: 256 eventos, 256 con prefijo, 0 ambiguos. El plan decia 36 y decia `handle-drag-start`; eran 40, y el canon (c25) dice que esos verbos son `pick` y `drop` — `handle-pick` y `handle-drop` ya existian en 10 y 4 componentes. `validateMorfo` cierra la puerta: un `events[].name` que no empiece por su familia ahora lanza. Visto fallar antes con un nombre pelado inyectado. Lo que el renombrado destapo, y va aqui tambien: - La receta del splitter enganchaba `commit-resize`, muerto desde `bd2e40366`. No casaba desde mayo y nadie chillo. Reescrita por FAMILIA, como slider y knob, y `eidos-lint` valida ahora el VALOR de `data-event*` contra el catalogo de morfos — el guard que lo habria cazado en su dia. - La familia `shift` era muda en el canal visual, contra su propia doctrina (c27: el cruce debe percibirse; c34 tipifica el «shift invisible»). Su mapa ya describia la firma que le faltaba y su sonido por defecto es `slide`. Ahora tiene firma direccional: sexto atributo del sello (`data-event-direction`, `forward`|`backward`, por emision) y deslizamiento de 320ms RTL-safe por `:dir()`. Medido: LTR -30px/+30px, RTL los invierte. - El sello de `shift-navigate` pasa del BOTON al `grid` en los cuatro calendarios. Medido: el boton recibia `contact-activate` y 8,5 ms despues —media trama— el `shift-navigate` pisaba la misma ranura y el `press-squeeze` moria sin pintar un fotograma. Una superficie, una ranura (A-36). - 101 contradicciones docs<->morfo adjudicadas con evidencia (git log, docs de decision, el componente vivo). Las docs desfasadas, corregidas; los nueve DEFECTOS de codigo obsoleto quedan abiertos y sin tocar. - `SoundDirection` -> `SoundContour`: era un contorno de tono, no un sentido, y habia tres cosas distintas deletreadas «direction». check en su linea base con 0 errores nuevos por diferencia de conjuntos · docs:check 0/0 · eidos-lint invalid 0 · el censo y las escenas de navegador medidas con raton real y rAF vivo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
(expand/collapse, present/dismiss, announce per intent, the `shift`
crossing per `data-event-direction`).
3. **Materials pattern** (the rule lives in the recipe but consumes ONLY
registered keyframes + `--motion-*` hooks) when the trigger is
irreducible to the generic: a semantic `data-state` of its own (card),
container-flag gating (timeline `data-live`), per-component axes (tabs
`data-motion` × orientation), soma's directional machinery
(navigation-menu from/to), or the per-component materialization of a
signal (metrics `value-flash` per intent). The reason is documented in
the rule.
Sibling guards already active: R-2.1/2.5/2.6 (color), R-2.7 (literal
typography), `recipe-css-contract.test.ts` (consumed tokens + TSC +
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
non-literal font/icon-size + variants vs type unions + **no phantom public
refs** — every no-fallback `var()` in a recipe value must exist in the
derived contract + **privates need a covering scope** — `--_*` refs never
from `root`, both 2026-07-07), the raw-z guard in `contracts.test.ts`.
The WIP tracks `words` / `palabras` / `chronos` are excluded from R-4.x
(same as the typographic guard) until they leave their parallel track.
## 5. Authoring checklist (before calling a recipe done)
1. Dimensional tokens with §1's names (logical axes, canonical heights).
2. Neutral hover = state-layer; valenced hover = palette tokens.
3. Zero literal `box-shadow` — everything through
`--shadow-*`/`--depth-*`/inset-ring.
4. Focus per archetype; not reinvented.
5. `opacity: var(--opacity-disabled)` on disabled.
6. `font-size`/`icon-size` 1:1 with the scale (or a documented canonical
exception).
7. Transitions with `--duration-*`/`--ease-*`; no perceptual-signature
`@keyframes`.
8. Spacing with `--space-*`, no hand-multiplied density.
9. If it's a list/menu → list-surface; if it floats → a
`--z-index-overlay-*` rung.
10. Every deviation carries its annotation (§3).
11. `node --import tsx/esm scripts/component-audit.ts --only {kebab}` with no
new R-4.x.

Powered by TurnKey Linux.