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
---
title: Component audit guide
type: guide
audience: human + agent
authority: binding — the pre-flight checklist before touching any component
status: current
source: migrated from web/routes/uix/lib/COMPONENT_AUDIT_GUIDE.md (2026-07-02, docs-book F7.5)
---
# Component audit guide
> **STOP. READ THIS BEFORE TOUCHING ANY COMPONENT.**
>
> This file is a **binding pre-flight checklist**. Every time you create,
> port, or modify a UIX component you MUST walk this guide front to back.
> Skipping steps produced the broken Layout Batch 1 (commit `9ec2a57a`)
> that had to be redone — a full day lost. Don't repeat the mistake.
## 0. The inviolable rule
Before touching `src/uix/{morfo,soma,sema,eidos}/components/{name}` or
`web/routes/uix/components/{name}/+page.svelte` :
1. **Read this entire file.** Yes, every time.
2. **Read [`demo-authoring.md`](./demo-authoring.md)** — the
demo template is locked.
3. **Read [`src/uix/eidos/components/README.md`](../../src/uix/eidos/components/README.md)** — the
eidos contract.
4. **Run the pre-flight audit in §3** against at least 4 reference
libraries. Tabular output, not prose.
5. **Get the user's sign-off on scope** before writing any code.
Agents you delegate to MUST be given this file in their brief. Their
brief must reference §3 (audit template) and §6 (anti-patterns)
explicitly.
docs+fix: old docs quarantined in docs/old-deprecated; STUMBLES doc-class fixes
Two user findings from the Knob build exercise (an agent building a new
component from the docs alone — STUMBLES.md).
Quarantine: the superseded fossils no longer share shelf space with the
live corpus. docs/old-deprecated/ (with an index README explaining what
lands there and pointing readers at docs/README.md) now holds the
executed audits and fix plans: fable_audit, fable-eidos-audit,
inherit_audit + inherit_fix_plan, ARCHETYPE_COHERENCE_AUDIT_2026-06-19
(still citable — the component-guide banner and the eidos components
README repoint to it), COMPONENT_COHERENCE_AUDIT. pendiente.md (a live
pending list, not a fossil) moved to docs/process/. docs-check treats
the folder as sealed chronicle (I1/I2 exempt; I6 skips its internal
links, as its README promises). Root-level *.md is now: README, CLAUDE,
AGENTS + the user's own working files.
STUMBLES fixes applied on the spot (the doc-class ones):
- #2 kind drift: the REAL enum is 'public' | 'private' | 'virtual'
(MorfoPartKind, 671/9/14 uses) — morfo.md omitted 'private', the
checklist invented 'internal' (0 uses). Both fixed; I2 gains the
phantom-'internal' guard. A-2.1's row now says what the audit script
actually checks (kebab only — the archetype may vary, the Toggle
provider-IS-trigger doctrine).
- #6: the langs catalog SHAPE (flat keys, per-language leaves, named
export, index registration) is now shown in morfo.md instead of only
its location.
- #8: component-audit s0 defines the minimum brief package as an
explicit 8-file list.
The engineering-class stumbles are registered as plan batches S1-S6
(generated vocabularies appendix, continuous-gesture trigger doctrine,
part-absent condition, Gesture.rotate, the soma->eidos CSS-var
contract, minor frictions). docs:check 0 errors, 11-warn baseline.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
**The minimum package** for a component-building brief is this exact file
list (titles are not enough — a package missing any of these produced a
non-conforming build in the Knob exercise, STUMBLES.md #8 ):
1. [`docs/building-a-component.md` ](../building-a-component.md ) — the route
2. [`docs/guides/component-guide.md` ](./component-guide.md ) — build steps + rules A1– A37
3. [`docs/guides/completion-checklist.md` ](./completion-checklist.md ) — acceptance
4. [`docs/guides/demo-authoring.md` ](./demo-authoring.md ) — the LOCKED demo template (D-1.x is error-level)
5. [`docs/guides/component-audit.md` ](./component-audit.md ) — this file
6. [`docs/CANON.md` ](../CANON.md ) — the ruling semantic vocabulary
7. [`docs/architecture/morfo.md` ](../architecture/morfo.md ) + [`sema.md` ](../architecture/sema.md ) + [`soma.md` ](../architecture/soma.md ) — the layer contracts
feat(docs): S1 — generated canonical-vocabulary appendix (STUMBLES #1)
An agent building a component from the docs-book alone could not assign
archetypes / holds / haptic kinds: the closed sets live only in code consts
and the corpus (correctly) forbids copying them into prose, so they were
invisible from the docs. STUMBLES #1, the top stumble.
Fix: docs/canon/vocabularies.md is GENERATED from the consts by
'npm run docs:vocabularies' — the ONE sanctioned place the lists are spelled
out (generated = no drift objection), which every other doc links to. It
covers part archetypes (with a one-line role each), sema families with their
default hold/persistence + per-intent overrides, verbs by family, intents,
the perceptual-duration scale, haptic kinds, the 33 palette scales, sizes,
variant archetypes, and the shared common.* strings.
- scripts/docs-vocabularies.ts imports the consts (robust — no fragile
JSDoc/regex parsing) and formats them; the generation is an exported
function so docs-check can compare.
- Added ARCHETYPE_DESCRIPTIONS to morfo/types.ts (co-located with
ARCHETYPE_VOCABULARY, satisfies Record<MorfoArchetype,string>) as the
authoritative one-liners the appendix reads — descriptions become
first-class data instead of inline union JSDoc a tool would have to parse.
- docs-check gains I7: it regenerates in-memory and fails if the committed
file drifts from the consts (negative-tested: corrupt -> error, regen ->
green). The appendix's own counts pass I1, so I1 doubles as a second guard.
- Linked from README (E2 canon row + two 'I want to' shortcuts), CANON.md
(doctrine here, enumerated lists there), architecture/morfo.md (pick a
part's archetype), and the component-audit minimum-package list (STUMBLES
#8).
docs:check 0 errors; npm run check unchanged in my files.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
8. [`docs/canon/vocabularies.md` ](../canon/vocabularies.md ) — the generated closed sets (archetypes, families+holds, verbs, intents, haptic kinds, scales, sizes, variants) you draw from
9. [`src/uix/eidos/components/README.md` ](../../src/uix/eidos/components/README.md ) — the eidos pattern
docs+fix: old docs quarantined in docs/old-deprecated; STUMBLES doc-class fixes
Two user findings from the Knob build exercise (an agent building a new
component from the docs alone — STUMBLES.md).
Quarantine: the superseded fossils no longer share shelf space with the
live corpus. docs/old-deprecated/ (with an index README explaining what
lands there and pointing readers at docs/README.md) now holds the
executed audits and fix plans: fable_audit, fable-eidos-audit,
inherit_audit + inherit_fix_plan, ARCHETYPE_COHERENCE_AUDIT_2026-06-19
(still citable — the component-guide banner and the eidos components
README repoint to it), COMPONENT_COHERENCE_AUDIT. pendiente.md (a live
pending list, not a fossil) moved to docs/process/. docs-check treats
the folder as sealed chronicle (I1/I2 exempt; I6 skips its internal
links, as its README promises). Root-level *.md is now: README, CLAUDE,
AGENTS + the user's own working files.
STUMBLES fixes applied on the spot (the doc-class ones):
- #2 kind drift: the REAL enum is 'public' | 'private' | 'virtual'
(MorfoPartKind, 671/9/14 uses) — morfo.md omitted 'private', the
checklist invented 'internal' (0 uses). Both fixed; I2 gains the
phantom-'internal' guard. A-2.1's row now says what the audit script
actually checks (kebab only — the archetype may vary, the Toggle
provider-IS-trigger doctrine).
- #6: the langs catalog SHAPE (flat keys, per-language leaves, named
export, index registration) is now shown in morfo.md instead of only
its location.
- #8: component-audit s0 defines the minimum brief package as an
explicit 8-file list.
The engineering-class stumbles are registered as plan batches S1-S6
(generated vocabularies appendix, continuous-gesture trigger doctrine,
part-absent condition, Gesture.rotate, the soma->eidos CSS-var
contract, minor frictions). docs:check 0 errors, 11-warn baseline.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
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
## 1. Reference libraries — who to consult and why
Every component must be audited against the relevant references below.
Pick at least 3, including Radix Themes when it's a visual primitive
and Ark UI / React Aria when it's a headless behaviour.
| Library | URL | Use for |
| --- | --- | --- |
| **Radix Themes** | https://radix-ui.com/themes | Visual primitives (Box / Flex / Grid / Container / Text). Canonical "Box-style split" architecture. |
| **Radix Primitives** | https://radix-ui.com/primitives | Headless behaviour (Dialog / Popover / Toast / DropdownMenu). ARIA reference. |
| **Chakra UI** | https://chakra-ui.com | Style-props heavy. Useful to spot props users expect on `Box` that we deliberately moved to `Flex` /`Grid`. |
| **Mantine** | https://mantine.dev | Strong typography + layout primitives (`Stack`, `Group` , `Container` , `Text` , `Title` ). |
| **MUI** | https://mui.com | Reference for `sx` prop, transitions, theme system. |
| **React Aria** | https://react-spectrum.adobe.com/react-aria | Industry-strength a11y. Use when keyboard or screen-reader contract is non-trivial. |
| **Ark UI** | https://ark-ui.com | Headless state machines (combobox, picker, tags-input). Mirror their event names when possible. |
| **Bits UI** | https://bits-ui.com | Svelte-native parallel to Radix Primitives — useful sanity check on component shape. |
| **shadcn/ui** | https://ui.shadcn.com | Mostly visual recipes layered on Radix. Useful for opinionated styling defaults. |
| **WAI-ARIA APG** | https://www.w3.org/WAI/ARIA/apg/patterns/ | The contract. Required reading for any role / aria audit. |
**Anti-rule:** "I'll just look at how air did it" is NOT a reference
audit. Air's port reflects the old architecture and the gaps from the
previous era. Audit fresh.
## 2. Layer responsibilities
Before deciding where a feature lives, recall the 4-layer architecture:
| Layer | Owns | Examples |
| --- | --- | --- |
| **morfo** | declarative contract: parts, data-attrs, ARIA, keyboard, events | `src/uix/morfo/components/{name}.ts` |
| **soma** | headless behaviour: state, effects, runtime providers, focus, ARIA wiring | `src/uix/soma/components/{name}/` |
| **sema** | perceptual side-effects: sound, motion, haptic projections of semantic events | `src/uix/sema/components/{name}/` |
| **eidos** | pure CSS visual recipe + `*Provider` wrappers that pipe `data-*` from morfo into the cascade | `src/uix/eidos/components/{name}/` |
**The 2-of-3 rule:** a morfo extension is only justified if at least
2 of 3 consumer layers (soma / sema / eidos) need it. (`feedback_2of3_rule`.)
**Visual-only primitives** (avatar, icon, all layout/typography
primitives) get `scope: ['eidos']` in their morfo — no soma, no sema.
Justify the 0-event surface in a header comment on the morfo file.
## 3. Pre-flight audit template
Before writing or porting a component, fill out this table. Don't
proceed without user sign-off.
```
### {ComponentName} audit — {YYYY-MM-DD}
#### Feature parity matrix
| Feature | Radix Themes | Chakra | Mantine | UIX (port plan) | Decision |
| --- | --- | --- | --- | --- | --- |
| {prop or feature} | {how their X handles it} | {…} | {…} | {what we plan} | implement / defer / drop |
#### Architectural choices
- Layer ownership (morfo / soma / sema / eidos): {…}
- Container vs item split: {if applicable, where each prop lives}
- Composition vs single-component: {does this share root with another primitive?}
- Sizes covered: {xs … xxl subset — declared in the recipe + types; parity rule in demo-authoring.md §6}
- Variants: {surface / outline / ghost / soft …}
- Color intent palette: {full / narrowed — justify}
#### Reference comparison summary
| Library | Closest equivalent | Difference vs UIX | Why we differ |
| --- | --- | --- | --- |
| {…} | {…} | {…} | {…} |
#### Decision log
- {gap}: {implement / defer / drop} — {reason}
```
Sign-off line at the bottom: `User signed scope on YYYY-MM-DD` .
## 4. Architectural rules (project-wide)
These are non-negotiable. Violations trigger a rewrite.
### 4.1 Radix-style item / container split
For layout primitives:
- **Container props** (`alignItems`, `justifyContent` , `flexDirection` ,
`flexWrap` , `templateColumns` , `gap` ) live on `<Flex>` / `<Grid>`
containers.
- **Item props** (`flex`, `grow` , `shrink` , `basis` , `order` ,
`alignSelf` , `justifySelf` , `placeSelf` , `gridColumn` , `gridRow` ,
`gridArea` ) live on `<Box>` .
Chakra puts everything on `<Box>` . We don't. Document the split in the
demo's lede + cross-references between primitives.
### 4.2 Composition over visibility props
Optional parts (Footer, Clear, Close, Header sub-items) **must not**
be controlled by `*Button` boolean props on the root. Visibility is
owned by composition — include the part to render it, omit to hide.
(Doctrine: `eidos/README.md` picker conventions, presence = visibility.)
### 4.3 Chip parity
Every chip-group control in a demo enumerates the **full** type union.
Truncated arrays are a contract bug. If the component narrows the
union deliberately (`Extract< …>`), the narrowing must be justified in
the component README. ([`demo-authoring.md`](./demo-authoring.md) §6.)
### 4.4 Size category cheatsheet
The shared `Size` scale is `xxs · xs · sm · md · lg · xl · xxl · full` .
Each component declares which steps it maps in the recipe + types —
that declaration IS the source. Demos reflect that narrowing 1:1
(parity rule: [`demo-authoring.md` ](./demo-authoring.md ) §6).
### 4.5 Per-event intent
Each morfo event's intent reflects its OWN evaluative load, not the
component's overall context. `close-save = fulfill` , `close-cancel =
absent`, `open = fromProp` . (`feedback_per_event_intent_intrinsic`.)
### 4.6 No re-export façades
`feedback_no_reexport_facades` + `feedback_no_reexport_shims` . If
`$libs/days` exposes a date util, the consumer imports `$libs/days`
directly. Do not add re-exports across layers.
### 4.7 Persistent label registries
When a component's contract requires displaying a label for a value
that may be unmounted (combobox after popover closes, multi-select
chips), the soma provider's value→label map MUST NOT be cleaned up on
item unmount — keep the entry around. Re-registration is idempotent
via `SvelteMap.set` . (Combobox `SelectedTags` bug from commit
`30e9517a` .)
### 4.8 Floating layer pattern
Anchored popovers default to **intrinsic width** + `align="start"` +
`sideOffset=6` . Do NOT set `matchAnchorWidth` unless the component
contract explicitly requires it. The visual gap from forcing
anchor-width is worse than the marginal alignment win. (Decision from
2026-05-22.)
### 4.9 Combobox-style keyboard
`<input>` keeps DOM focus; highlight moves virtually via
`aria-activedescendant` . Arrow keys cycle, Enter commits, Escape
closes, Tab moves out of the component (does NOT enter the listbox).
Pattern: WAI-ARIA combobox. (`feedback_combobox_keyboard`, SearchField
demo 2026-05-22.)
### 4.10 Chakra-style multi-select chips
When a combobox / multi-select renders selected values as chips, the
chips go ABOVE the input control (Chakra v3 multi pattern). Reason:
the popover opens downward and would cover chips that sit below the
input. Do NOT inline chips inside the control — MUI Autocomplete's
inline pattern fights wrap behavior. (Decision from `20709caf` .)
### 4.11 No literal typography in recipes (`R-2.7`)
Component CSS recipes consume tokens, never literal values, for
`font-size` / `font-weight` / `line-height` / `letter-spacing` /
`font-family` . The audit script flags violations with `warn`
severity.
**Why:** the system vertebrates via the foundation token chain:
```
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
typography.ts styles → --style-{name}-* → consumed directly by recipes
(+ --leading-ui, config data)
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
→ --field-*-* (recipe tokens)
→ component CSS
```
A literal in a recipe breaks the chain — changing the foundation
no longer propagates. See [`architecture/eidos.md` ](../architecture/eidos.md )
§ "Typographic vertebration" for the full architecture (why two anchors,
why recipes don't read `--style-{name}-*` directly, comparison vs Radix
Themes / Chakra / Mantine / MUI).
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
Allowed escape valves:
- `var(...)` wrapping the value
- numeric identities: `0` , `0px` , `0em` , `0rem` , `1`
- keywords: `inherit` , `initial` , `unset`
- explicit opt-out via trailing `/* literal: <reason> */` comment
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
### 4.12 Run the contract audit script
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
The repo ships `scripts/component-audit.ts` (alias `npm run
component:audit`). It is the **canonical verification tool** for
contract compliance — it walks every component, parses morfo declarations,
cross-checks against eidos recipes + demos, and emits a markdown report
at `tmp/component-audit.md` .
```bash
npm run component:audit # full run
node --import tsx/esm scripts/component-audit.ts --only NAME # single component
node --import tsx/esm scripts/component-audit.ts --severity error
```
A component is only "done" when its row in the scoreboard reads
**PASS** with 0 errors. Warnings should be triaged in the audit log
(§8). The audit script enforces:
- `E-2.x` morfo / eidos / demo / README presence
- `D-7.4` demo chip-array parity vs type union
- `A-3.x` keyboard ↔ events declarative consistency
- `R-1.x` morfo `data-*` attrs styled in eidos recipe
- `F-1.x` README structure (Baseline / Comparativa / Decisiones / Gaps)
Run it before every commit that touches a component. Block the merge
on any error.
## 5. The demo template is locked
Read [`demo-authoring.md` ](./demo-authoring.md ). The full template is
mandatory — the tab set (v2: 9 tabs), the always-on stage + trace, the
harness modules, layer-grouped controls, snippet parity, and the
always-rendered contract tabs are all defined THERE; this guide does not
copy the list (copies drift — the v1 6-tab enumeration that used to live
here went stale against the v2 template).
The canary is **button** (`web/routes/uix/components/button/+page.svelte`,
the v2 worked reference). Layout primitives have their own canaries —
**box** and **flex** (see §7).
## 6. Anti-patterns (failed approaches — don't repeat)
These produced rework or rejected commits. Don't propose them again
without naming the reason.
| Anti-pattern | Where it failed | Why |
| --- | --- | --- |
| Shallow 100-line demo "marketing page" | Layout Batch 1 v1 (`9ec2a57a`) | Violates `DEMO_AUTHORING_GUIDE` . Reverted + redone. |
| `matchAnchorWidth` on date-picker popover | 2026-05-22 morning | Visual gap when popover content narrower than input. User explicitly abandoned. |
| Visibility via `*Button` boolean props | DatePicker pre-`69`, removed | Inverts composition. Use `{#if showX}` around the part instead. |
| Inline chips inside Combobox control | Combobox v1– v3 (2026-05-22) | Fights flex-wrap; popover covers chips. Switched to band-above-Control (Chakra). |
| `onpointerdown` + `preventDefault` for item picks | SearchField v1 | Eats the synthetic click — onclick never fires. Use plain `<button onclick>` . |
| `flex: 1 1 100%` on input inside flex-wrap container | Combobox v2 | Forces wrap to new row even with chips fitting. Use `flex: 1 1 4rem` and let it share rows. |
| Re-registering child labels and unregistering on unmount | Combobox SelectedTags v1 | Labels disappear when popover closes. Register on mount, never unregister. |
| Auto-rendering everything in a wrapper | Combobox.SelectedTags v1 | Less customizable than explicit composition. Provide a default snippet, allow override. |
| `inputRef.focus()` after picking without guard | SearchField popover v1 | Re-fires `onfocusin` → reopens popover. Use `justCommitted` flag + microtask reset. |
| Agent runs `git reset` mid-session | Layout Batch 2 v1 worktree | Lost reported work. Brief MUST say "DO NOT git reset, stash, or checkout files." |
| Skipping reference audit before porting | Layout Batch 1 v1 | Missed `alignContent` , `columns/rows` shorthand, `grow` , `divider` slot. Always audit first. |
## 7. Canonical canaries
When unsure how to shape a new component, copy and adapt from these:
| Domain | Canary | Path |
| --- | --- | --- |
| Overlay with state | Drawer | `web/routes/uix/components/drawer/+page.svelte` |
| Form input with composite parts | Search field | `web/routes/uix/components/search-field/+page.svelte` |
| Visual-only primitive (Box-style) | Box | `web/routes/uix/components/box/+page.svelte` |
| Visual-only container with own props | Flex | `web/routes/uix/components/flex/+page.svelte` |
| Multi-state composite (header / body / footer) | Date picker | `web/routes/uix/components/date-picker/+page.svelte` |
| Passive headless (image / svg) | Avatar | `web/routes/uix/components/avatar/+page.svelte` |
## 8. Audit log
Running log of completed reference audits. Add a row when you sign
off scope with the user. Use the §3 template; link the commit that
closed the work.
| Date | Component | Audited against | Commit | Notes |
| --- | --- | --- | --- | --- |
| 2026-05-22 | Combobox (multi tags) | radix-themes, chakra, mui (Autocomplete), mantine | `20709caf` | Chose Chakra v3 band-above-control after rejecting MUI inline |
| 2026-05-22 | Layout Batch 1 (Box) | radix-themes, chakra, mantine | `66c6897c` | Moved gridArea/Column/Row from Grid → Box (Radix split). Added placeSelf. |
| 2026-05-22 | Layout Batch 1 (Flex + 6 others) | radix-themes, chakra, mantine | `591b0885` | **Surface-level only — see "Known gaps" below. Re-audit before next round.** |
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
### Known gaps from Layout Batch 1 (HISTORICAL — 2026-05-22 snapshot)
> Superseded by the 2026-07 component re-audit programme (the full matrix
> went green — `docs/audit/components/_cierre.md`). The gaps below are the
> frozen record of that moment; CURRENT gaps live in each component's
> dossier (`## Gaps`, with disposition).
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
| Primitive | Missing feature | Reference | Priority |
| --- | --- | --- | --- |
| Flex | `alignContent` (multi-line cross-axis align) | Radix, Chakra | should |
| Grid | `columns` / `rows` shorthand (e.g. `columns="3"` → `repeat(3, 1fr)` ) | Radix Themes | should |
| Grid | `inline` boolean (display: inline-grid) | parity with Flex.inline | nice |
| Grid | `alignContent` | Radix, Chakra | should |
| Stack | `divider` slot (separator between items) | Chakra Stack | nice |
| Stack | `HStack` / `VStack` helpers | Chakra ergonomics | nice |
| Group | `grow` boolean (children fill width equally) | Mantine Group | should |
| Group | `preventGrowOverflow` | Mantine Group | nice |
| Wrap | `shouldWrapChildren` (auto-wrap each child in WrapItem) | Chakra Wrap | nice |
| Container | `fluid` mode (no max-width) | Mantine | should |
| Section | — | none missing | — |
## 9. Pre-port checklist (copy this into your task plan)
```
[ ] Read component-audit.md (this file)
[ ] Read demo-authoring.md
[ ] Read eidos/components/README.md
[ ] Identify reference libraries to audit against (§1)
[ ] Fill out the §3 audit template
[ ] Present scope table + decisions to user
[ ] Get user sign-off in writing
[ ] Implement: morfo → soma (if needed) → eidos → demo
[ ] Write src/uix/eidos/components/{name}/README.md with the
required sections (Baseline / Comparativa / Decisiones / Gaps)
[ ] Run `npm run component:audit` — verdict MUST be PASS
[ ] Run `npm run check` — 0 errors required
[ ] Visual walk: every Live control changes the stage; every Sema
`▶ play` button fires; snippets reflect control values
[ ] Add row to §8 audit log with commit hash
[ ] Update §6 anti-patterns if a new failure mode emerged
```
## 10. When in doubt
Stop and ask the user. The cost of a clarifying question is always
lower than the cost of redoing 8 components.