feat(eidos): recipe contract R-4.x + motion-channel migration 15/15 + theme-builder fixes + inventory decisions
Fable audit follow-through (fable_audit.md + fable-eidos-audit.md):
- RECIPE_CONTRACT.md (E2 canon): the transversal systems every recipe must
consume, enforced by component-audit R-4.1-4.6 (all at error; escape
valves /* literal */ + /* functional */; WIP tracks excluded). Stale
audit rules fixed against the current architecture (E-2.2 wrapper
imports, D-1.2 v2 9-tab union, D-3.1 single snippet, TabsVariant
mirror) - verdicts went 0/117/15 -> 75/50/5.
- Motion migration 15/15: recipes off local @keyframes onto the channel -
preset stamps (dropdown/context/select/combobox/tooltip/link-preview/
clipboard), new expand/collapse + value-flash signatures, shared-axis
reverse pair, delayed-open open-alias in the preset trigger
(PRESET_STATE_ALIASES), materials pattern for irreducible triggers
(card/timeline/tabs/nav-menu/metrics). Duration/scale hooks keep every
recipe's tuned values.
- buildScheme (fase C): full a1..a12 alpha ramp per role (was a2/a3 -
stale alphas after applyColorScheme), intentSeeds so temper starts from
the ACTIVE theme's intent mapping (risk stays orange), alpha background
self-derived from the scheme's own neutral step 1 (was hardcoded
#fff/#111); mode now forces the donor variant.
- Inventory decisions (fase D): semanticTracking axis removed (all-zero),
border-hover slot dropped (0 consumers), separator slot adopted across
line dividers (step 6, Radix divider tone), size-bundle consumption
pilot on toggle (canonical coordinates consumed, deliberate deviations
kept visible).
- Docs: building-a-component.md (the one door, 9-phase route + known
traps), PLAN-docs-reconciliation.md (fase 6 kickoff for a fresh
session), THEMING wiring updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
---
|
|
|
|
|
|
title: Building a Component — the one door
|
|
|
|
|
|
type: guide
|
|
|
|
|
|
audience: human + agent
|
|
|
|
|
|
authority: navigational — THE entry point for building a new component; content lives in the linked docs
|
|
|
|
|
|
status: current
|
|
|
|
|
|
related:
|
docs(book): F7.5 (1/2) — checklist + audit guide + demo guide moved to docs/guides/
Three English guides moved verbatim (copy + link touch-ups, no
translation): COMPONENT_COMPLETION_CHECKLIST -> guides/
completion-checklist.md, COMPONENT_AUDIT_GUIDE -> guides/
component-audit.md, DEMO_AUTHORING_GUIDE -> guides/demo-authoring.md.
Tooling moved with its doc in the same pass: docs-check I5 now parses
the checklist at the new path (verified: 0 errors, rule tables found)
and component-audit.ts's two path strings updated. One reconciliation
in the audit guide's s5, applied as link-don't-copy: the copied v1
6-tab template list (stale vs the v2 9-tab guide it links, drawer
canary vs button) is replaced by a pointer to demo-authoring.md; the
domain canaries in s7 stay. demo-authoring's harness links now reach
into web/routes/uix/lib/ (the modules stay with the code); the deleted
pendiente_color_demos.md reference is marked TODO(reconcile). Corpus
swept: building-a-component (frontmatter related: + phases 6/8),
authoring, architecture/soma, theming/reference s5-parity,
testing-and-tooling, getting-started, README (E4 rows + component-audit
row added + two stale F7.3 paths in 'I want to' fixed). Remaining
warns = links to component-guide.md, which lands in F7.5 (2/2).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
acceptance: docs/guides/completion-checklist.md (when is it done — machine-audited)
|
|
|
|
|
|
soma: docs/guides/component-guide.md (the deepest phase doc)
|
feat(eidos): recipe contract R-4.x + motion-channel migration 15/15 + theme-builder fixes + inventory decisions
Fable audit follow-through (fable_audit.md + fable-eidos-audit.md):
- RECIPE_CONTRACT.md (E2 canon): the transversal systems every recipe must
consume, enforced by component-audit R-4.1-4.6 (all at error; escape
valves /* literal */ + /* functional */; WIP tracks excluded). Stale
audit rules fixed against the current architecture (E-2.2 wrapper
imports, D-1.2 v2 9-tab union, D-3.1 single snippet, TabsVariant
mirror) - verdicts went 0/117/15 -> 75/50/5.
- Motion migration 15/15: recipes off local @keyframes onto the channel -
preset stamps (dropdown/context/select/combobox/tooltip/link-preview/
clipboard), new expand/collapse + value-flash signatures, shared-axis
reverse pair, delayed-open open-alias in the preset trigger
(PRESET_STATE_ALIASES), materials pattern for irreducible triggers
(card/timeline/tabs/nav-menu/metrics). Duration/scale hooks keep every
recipe's tuned values.
- buildScheme (fase C): full a1..a12 alpha ramp per role (was a2/a3 -
stale alphas after applyColorScheme), intentSeeds so temper starts from
the ACTIVE theme's intent mapping (risk stays orange), alpha background
self-derived from the scheme's own neutral step 1 (was hardcoded
#fff/#111); mode now forces the donor variant.
- Inventory decisions (fase D): semanticTracking axis removed (all-zero),
border-hover slot dropped (0 consumers), separator slot adopted across
line dividers (step 6, Radix divider tone), size-bundle consumption
pilot on toggle (canonical coordinates consumed, deliberate deviations
kept visible).
- Docs: building-a-component.md (the one door, 9-phase route + known
traps), PLAN-docs-reconciliation.md (fase 6 kickoff for a fresh
session), THEMING wiring updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
# Building a Component — the one door
|
|
|
|
|
|
|
|
|
|
|
|
Read **this file first and only this file**. Building a component crosses the four
|
|
|
|
|
|
layers and the theming system; the knowledge lives in eight documents that each own
|
|
|
|
|
|
one phase. Nobody should have to discover that chain by archaeology — this doc IS
|
|
|
|
|
|
the chain. Each phase names **the one document to read**, what you produce, and the
|
|
|
|
|
|
guard that verifies it.
|
|
|
|
|
|
|
|
|
|
|
|
Role note (authoring rule 4 — one source per concern): this doc contains **no
|
|
|
|
|
|
content**, only sequencing. If a phase doc and this table disagree about order,
|
|
|
|
|
|
this doc wins on _order_; the phase doc wins on _content_.
|
feat(eidos): recipe contract R-4.x + motion-channel migration 15/15 + theme-builder fixes + inventory decisions
Fable audit follow-through (fable_audit.md + fable-eidos-audit.md):
- RECIPE_CONTRACT.md (E2 canon): the transversal systems every recipe must
consume, enforced by component-audit R-4.1-4.6 (all at error; escape
valves /* literal */ + /* functional */; WIP tracks excluded). Stale
audit rules fixed against the current architecture (E-2.2 wrapper
imports, D-1.2 v2 9-tab union, D-3.1 single snippet, TabsVariant
mirror) - verdicts went 0/117/15 -> 75/50/5.
- Motion migration 15/15: recipes off local @keyframes onto the channel -
preset stamps (dropdown/context/select/combobox/tooltip/link-preview/
clipboard), new expand/collapse + value-flash signatures, shared-axis
reverse pair, delayed-open open-alias in the preset trigger
(PRESET_STATE_ALIASES), materials pattern for irreducible triggers
(card/timeline/tabs/nav-menu/metrics). Duration/scale hooks keep every
recipe's tuned values.
- buildScheme (fase C): full a1..a12 alpha ramp per role (was a2/a3 -
stale alphas after applyColorScheme), intentSeeds so temper starts from
the ACTIVE theme's intent mapping (risk stays orange), alpha background
self-derived from the scheme's own neutral step 1 (was hardcoded
#fff/#111); mode now forces the donor variant.
- Inventory decisions (fase D): semanticTracking axis removed (all-zero),
border-hover slot dropped (0 consumers), separator slot adopted across
line dividers (step 6, Radix divider tone), size-bundle consumption
pilot on toggle (canonical coordinates consumed, deliberate deviations
kept visible).
- Docs: building-a-component.md (the one door, 9-phase route + known
traps), PLAN-docs-reconciliation.md (fase 6 kickoff for a fresh
session), THEMING wiring updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## The route
|
|
|
|
|
|
|
|
|
|
|
|
| Phase | You produce | Read THIS | Verified by |
|
|
|
|
|
|
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
|
|
|
|
| **0 · Decide** | comparison table vs ark/bits/radix (+ react-aria), membership call (soma or eidos-native), compose-first check | [`component-guide.md`](./guides/component-guide.md) §Before You Start (1–4) · membership: [`architecture/soma.md`](./architecture/soma.md) §2 | reviewer — the table goes in the component README (phase 7) |
|
|
|
|
|
|
| **1 · Morfo** | `src/uix/morfo/components/{kebab}.ts` — parts, data/aria, keyboard, events (family/verb/intent/sequence), `texts`, `expression` | [`morfo/README.md`](./architecture/morfo.md) §Authoring a new morfo · vocabulary: [`CANON.md`](./CANON.md) | `validateMorfo` + `npm run morfo:vocabulary` + audit `A-*` |
|
|
|
|
|
|
| **2 · Soma** | `{kebab}-provider.svelte.ts` + thin wrappers + `types.ts` + provider test | [`component-guide.md`](./guides/component-guide.md) (patterns + rules A1–A37) | provider test + `npm run check` |
|
|
|
|
|
|
| **3 · Sema** | pack `src/uix/sema/components/{kebab}.ts` (or explicit `expression: 'family-default'`) — selectors via `semaSelector` ONLY | [`architecture/sema.md`](./architecture/sema.md) §packs + §typed builder · pack-vs-default criteria: [`book-deviations.md`](./decisions/book-deviations.md) D.4 | `npm run morfo:vocabulary` (expression coverage) |
|
|
|
|
|
|
| **4 · Eidos wrapper** | `eidos/components/{kebab}/` — root + attached parts (option C), visual props | [`eidos/components/README.md`](../src/uix/eidos/components/README.md) (the 7 hard rules) | `component-api-contract` test + audit `E-*` |
|
|
|
|
|
|
| **5 · Recipe + theming** | recipe tokens in `lib/recipes/base.ts` + `{kebab}.css` | [`theming/guide.md`](./theming/guide.md) pasos 1–6 · **mandatory contract**: [`canon/recipe-contract.md`](./canon/recipe-contract.md) · scope (if `data-color`): [`canon/tsc.md`](./canon/tsc.md) | `npm run generate:eidos-css` + `recipe-css-contract` test + audit `R-*` (R-4.x = the contract) |
|
|
|
|
|
|
| **6 · Demo** | `web/routes/uix/components/{kebab}/+page.svelte` — v2 9-tab layout | [`demo-authoring.md`](./guides/demo-authoring.md) | audit `D-*` + `npm run smoke` |
|
|
|
|
|
|
| **7 · Component README** | Baseline · Comparativa (≥3 refs) · Decisiones · Gaps-with-disposition · Sema events table | template = any recent PASS component's README; sections audited | audit `F-*` |
|
|
|
|
|
|
| **8 · Acceptance** | nothing new — the component passes | [`completion-checklist.md`](./guides/completion-checklist.md) | `npm run component:audit --only {kebab}` + `npm run morfo:check` + `eidos-lint` |
|
feat(eidos): recipe contract R-4.x + motion-channel migration 15/15 + theme-builder fixes + inventory decisions
Fable audit follow-through (fable_audit.md + fable-eidos-audit.md):
- RECIPE_CONTRACT.md (E2 canon): the transversal systems every recipe must
consume, enforced by component-audit R-4.1-4.6 (all at error; escape
valves /* literal */ + /* functional */; WIP tracks excluded). Stale
audit rules fixed against the current architecture (E-2.2 wrapper
imports, D-1.2 v2 9-tab union, D-3.1 single snippet, TabsVariant
mirror) - verdicts went 0/117/15 -> 75/50/5.
- Motion migration 15/15: recipes off local @keyframes onto the channel -
preset stamps (dropdown/context/select/combobox/tooltip/link-preview/
clipboard), new expand/collapse + value-flash signatures, shared-axis
reverse pair, delayed-open open-alias in the preset trigger
(PRESET_STATE_ALIASES), materials pattern for irreducible triggers
(card/timeline/tabs/nav-menu/metrics). Duration/scale hooks keep every
recipe's tuned values.
- buildScheme (fase C): full a1..a12 alpha ramp per role (was a2/a3 -
stale alphas after applyColorScheme), intentSeeds so temper starts from
the ACTIVE theme's intent mapping (risk stays orange), alpha background
self-derived from the scheme's own neutral step 1 (was hardcoded
#fff/#111); mode now forces the donor variant.
- Inventory decisions (fase D): semanticTracking axis removed (all-zero),
border-hover slot dropped (0 consumers), separator slot adopted across
line dividers (step 6, Radix divider tone), size-bundle consumption
pilot on toggle (canonical coordinates consumed, deliberate deviations
kept visible).
- Docs: building-a-component.md (the one door, 9-phase route + known
traps), PLAN-docs-reconciliation.md (fase 6 kickoff for a fresh
session), THEMING wiring updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
|
|
|
|
|
|
Cross-phase invariants you will hit in every phase: the layer boundaries
|
|
|
|
|
|
([`architecture/active-architecture.md`](./architecture/active-architecture.md) §7 hard rules) and the
|
|
|
|
|
|
2-of-3 rule for extending morfo ([`morfo/README.md`](./architecture/morfo.md)).
|
feat(eidos): recipe contract R-4.x + motion-channel migration 15/15 + theme-builder fixes + inventory decisions
Fable audit follow-through (fable_audit.md + fable-eidos-audit.md):
- RECIPE_CONTRACT.md (E2 canon): the transversal systems every recipe must
consume, enforced by component-audit R-4.1-4.6 (all at error; escape
valves /* literal */ + /* functional */; WIP tracks excluded). Stale
audit rules fixed against the current architecture (E-2.2 wrapper
imports, D-1.2 v2 9-tab union, D-3.1 single snippet, TabsVariant
mirror) - verdicts went 0/117/15 -> 75/50/5.
- Motion migration 15/15: recipes off local @keyframes onto the channel -
preset stamps (dropdown/context/select/combobox/tooltip/link-preview/
clipboard), new expand/collapse + value-flash signatures, shared-axis
reverse pair, delayed-open open-alias in the preset trigger
(PRESET_STATE_ALIASES), materials pattern for irreducible triggers
(card/timeline/tabs/nav-menu/metrics). Duration/scale hooks keep every
recipe's tuned values.
- buildScheme (fase C): full a1..a12 alpha ramp per role (was a2/a3 -
stale alphas after applyColorScheme), intentSeeds so temper starts from
the ACTIVE theme's intent mapping (risk stays orange), alpha background
self-derived from the scheme's own neutral step 1 (was hardcoded
#fff/#111); mode now forces the donor variant.
- Inventory decisions (fase D): semanticTracking axis removed (all-zero),
border-hover slot dropped (0 consumers), separator slot adopted across
line dividers (step 6, Radix divider tone), size-bundle consumption
pilot on toggle (canonical coordinates consumed, deliberate deviations
kept visible).
- Docs: building-a-component.md (the one door, 9-phase route + known
traps), PLAN-docs-reconciliation.md (fase 6 kickoff for a fresh
session), THEMING wiring updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Known traps
|
|
|
|
|
|
|
docs(direction): el contrato pasa a ser canon, y el corpus deja de contradecirlo
El eje estaba cerrado en CODIGO y no en DOCUMENTACION. El contrato vivia solo en
`CONTINUE-direction.md`, un handoff que `docs/README.md` declara «never a source
of truth». Un capitulo E2 lo fija ahora: `docs/canon/direction-contract.md`.
Va a canon y no a arquitectura por la misma razon que `recipe-contract.md`: es
normativo y tiene guard ejecutable. Cubre la cadena y donde corre cada eslabon;
por que `undefined` no es `'ltr'`; las DOS atributos —`dir` crudo (nativo, lo que
`:dir()` mira) y `data-dir` resuelto (opt-in, para recetas que necesitan un hook
incondicional)—; cuando el estampado es OBLIGATORIO; la doctrina de selector; las
trampas que el guard no ve; que espeja y que no; y la mitad global de prefs.
EL CORPUS SE CONTRADECIA en cuatro sitios, y dos de ellos ENSENABAN mal:
- `component-guide.md:372` daba la cadena como `prefs → 'ltr'`, DOS eslabones,
mandando al wrapper a leer prefs directamente. Eso excluye la prop.
- `soma-architecture.md` §3.4 presentaba el resolutor a pelo (`soma.prefs.getDir()`
dentro del provider) como LA forma de obtener la direccion — justo lo que el eje
retiro del catalogo.
- `html.ts` ensenaba en su JSDoc `dir?: 'ltr' | 'rtl'`, el union a mano que la
regla E-2.5 ahora prohibe. El mecanismo que ensena (extender para estrechar una
clave HTML) es correcto y sobrevive; solo cambia el ejemplo.
- `active-architecture.md:416` no listaba `lang` en la proyeccion, contra
`contracts.ts` y contra su propio test de frontera. Igual `prefs/README.md`.
Y no lo mencionaba en absoluto: el glosario (`activeDir`, `resolvedDir`,
`data-dir`, RTL-1, el escape `rtl-physical:`), `eidos.md` (RTL-1 es el TERCER
guard de deriva y faltaba junto al builder tipado y eidos-lint), `soma.md`,
`building-a-component.md` (§Known traps es exactamente donde va «la prop mueve la
matematica y deja la pintura atras»), y la tabla de atributos de la arquitectura,
que afirma «todo lo que viaja entre capas viaja por atributos» y omitia `dir`.
El checklist gana cuatro reglas y el guard que faltaba (`rtl:check` no estaba en
la matriz de aceptacion pese a que la tabla canon lo nombra), mas un recorte en
E-3.5: «todas las props visuales mapean a `data-{prop}`» leia como mandato de
estampar `data-dir`, que `:dir()` no puede ver.
DIVERGENCIA DECLARADA (§7): la familia `chart` no tiene prop ni provider y
resuelve leyendo `getComputedStyle(node).direction`. Es el unico sitio del
catalogo que hace lo que §1 prohibe. Se registra en vez de esconderse.
Tres verificadores adversariales sobre el barrido; sus hallazgos, corregidos:
RTL-1 estaba anunciado como guard de TODO el capitulo (solo cubre §4) · «nunca
lee al padre» era absoluto y borraba la composicion sancionada en el punto de
llamada · el estampado se afirmaba incondicional en un sitio y condicional en
otro · las cuatro filas nuevas usaban una aplicabilidad que ni la leyenda ni
`component-audit.ts` conocen.
`docs:check` 0 errores sobre 564 docs. `check` 77 = la linea base.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
**The `dir` prop moves the maths and leaves the paint behind.** A component can
|
|
|
|
|
|
accept `dir`, run the chain and flip its arrow keys while nothing mirrors: a
|
|
|
|
|
|
recipe's `:dir()` branch matches the direction the element _inherited_ unless
|
docs(direction): el contrato pasa a ser canon, y el corpus deja de contradecirlo
El eje estaba cerrado en CODIGO y no en DOCUMENTACION. El contrato vivia solo en
`CONTINUE-direction.md`, un handoff que `docs/README.md` declara «never a source
of truth». Un capitulo E2 lo fija ahora: `docs/canon/direction-contract.md`.
Va a canon y no a arquitectura por la misma razon que `recipe-contract.md`: es
normativo y tiene guard ejecutable. Cubre la cadena y donde corre cada eslabon;
por que `undefined` no es `'ltr'`; las DOS atributos —`dir` crudo (nativo, lo que
`:dir()` mira) y `data-dir` resuelto (opt-in, para recetas que necesitan un hook
incondicional)—; cuando el estampado es OBLIGATORIO; la doctrina de selector; las
trampas que el guard no ve; que espeja y que no; y la mitad global de prefs.
EL CORPUS SE CONTRADECIA en cuatro sitios, y dos de ellos ENSENABAN mal:
- `component-guide.md:372` daba la cadena como `prefs → 'ltr'`, DOS eslabones,
mandando al wrapper a leer prefs directamente. Eso excluye la prop.
- `soma-architecture.md` §3.4 presentaba el resolutor a pelo (`soma.prefs.getDir()`
dentro del provider) como LA forma de obtener la direccion — justo lo que el eje
retiro del catalogo.
- `html.ts` ensenaba en su JSDoc `dir?: 'ltr' | 'rtl'`, el union a mano que la
regla E-2.5 ahora prohibe. El mecanismo que ensena (extender para estrechar una
clave HTML) es correcto y sobrevive; solo cambia el ejemplo.
- `active-architecture.md:416` no listaba `lang` en la proyeccion, contra
`contracts.ts` y contra su propio test de frontera. Igual `prefs/README.md`.
Y no lo mencionaba en absoluto: el glosario (`activeDir`, `resolvedDir`,
`data-dir`, RTL-1, el escape `rtl-physical:`), `eidos.md` (RTL-1 es el TERCER
guard de deriva y faltaba junto al builder tipado y eidos-lint), `soma.md`,
`building-a-component.md` (§Known traps es exactamente donde va «la prop mueve la
matematica y deja la pintura atras»), y la tabla de atributos de la arquitectura,
que afirma «todo lo que viaja entre capas viaja por atributos» y omitia `dir`.
El checklist gana cuatro reglas y el guard que faltaba (`rtl:check` no estaba en
la matriz de aceptacion pese a que la tabla canon lo nombra), mas un recorte en
E-3.5: «todas las props visuales mapean a `data-{prop}`» leia como mandato de
estampar `data-dir`, que `:dir()` no puede ver.
DIVERGENCIA DECLARADA (§7): la familia `chart` no tiene prop ni provider y
resuelve leyendo `getComputedStyle(node).direction`. Es el unico sitio del
catalogo que hace lo que §1 prohibe. Se registra en vez de esconderse.
Tres verificadores adversariales sobre el barrido; sus hallazgos, corregidos:
RTL-1 estaba anunciado como guard de TODO el capitulo (solo cubre §4) · «nunca
lee al padre» era absoluto y borraba la composicion sancionada en el punto de
llamada · el estampado se afirmaba incondicional en un sitio y condicional en
otro · las cuatro filas nuevas usaban una aplicabilidad que ni la leyenda ni
`component-audit.ts` conocen.
`docs:check` 0 errores sobre 564 docs. `check` 77 = la linea base.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
the provider stamps the attribute. Phase 2 and phase 5 decide that together —
|
|
|
|
|
|
[`canon/direction-contract.md`](./canon/direction-contract.md).
|
|
|
|
|
|
|
|
|
|
|
|
The superseded material this table used to warn about was fixed by
|
|
|
|
|
|
the 2026-07 reconciliation pass (morfo `texts:`, the degraded
|
|
|
|
|
|
GUIA_IMPLEMENTACION, THEMING §13/§14, soma deps, the archetype count), and
|
|
|
|
|
|
`npm run docs:check` now guards the recurrence classes mechanically
|
|
|
|
|
|
(vocabulary counts, phantom fields, dependency claims, checklist↔audit sync,
|
|
|
|
|
|
links). If you find a doc contradicting the code, that IS a bug: **code +
|
|
|
|
|
|
[`CANON.md`](./CANON.md) win** — fix the doc or open an issue (THEMING.md's
|
|
|
|
|
|
own footer says the same).
|