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/guides/completion-checklist.md

48 KiB

title type audience authority status source
Component completion checklist guide human + agent canonical — the acceptance matrix for a component being done current migrated from src/uix/COMPONENT_COMPLETION_CHECKLIST.md (2026-07-02, docs-book F7.5)

Component completion checklist

Doctrinal criteria for considering a UIX component done across all four layers (Morfo · Soma · Sema · Eidos), its recipe CSS, and its demo page.

Source of truth — architecture/active-architecture.md, decisions/guia-semantica-historica.md (historical seed), demo-authoring.md.

Machine-validated by scripts/component-audit.ts. Run via npm run component:audit [name]?. Outputs a markdown report at tmp/component-audit.md.

This is the acceptance matrix — the criteria for done, not a build guide. For HOW to build a component (the ordered authoring steps + rationale rules A1–A37), see component-guide.md. The two are a complementary pair, not duplicate checklists.

How to read this

Each rule has a severity, an applicability, and an enforcement:

  • Severity:
    • error — blocks the component from being considered done.
    • warn — should be fixed but not blocking.
    • info — informational, no remediation expected.
  • Applicability:
    • all — every public component.
    • interactive — components with user actions (most). Identified by morfo.events.length > 0 OR morfo.parts.*.keyboard.length > 0.
    • passive — purely structural / display components (icon, avatar, breadcrumb, meter, progress). Allowed 0 events only after README justifies it.
  • Enforcement — who verifies the rule. Declaring a rule here does NOT imply the audit script checks it; this column makes the gap explicit:
    • audit — implemented in scripts/component-audit.ts (the report prints the same rule ID).
    • tool:{name} — enforced by another script/test (e.g. tool:morfo:check, tool:smoke, tool:check for tsc/svelte-check).
    • manual — human review; no mechanical check exists yet. Candidates for promotion to audit are welcome (see §I).

A. Morfo declaration

The morfo is DNA. If it's incomplete, every downstream layer is incomplete.

A1 · Basics

ID Rule Severity Applicability Enforcement
A-1.1 Exports a single {Name}Morfo const satisfying Morfo error all audit
A-1.2 Has name, kebab, scope: ['soma', ...] declared error all audit
A-1.3 Has texts.label as a valid idlangref (#?components.{kebab}.label|Fallback), catalog entry in langs/components/{kebab}.ts error all audit
A-1.4 If interactive: apg URL declared pointing at the relevant W3C ARIA pattern warn interactive audit

A2 · Parts

ID Rule Severity Applicability Enforcement
A-2.1 Has at least one part with kebab: 'provider' (the audit checks the kebab only — the archetype may vary: trigger when the Provider IS the interactive element (Toggle/Switch), image for icon, …) error all audit
A-2.2 Every part declares kebab, archetype, kind: 'public' | 'private' | 'virtual' (MorfoPartKind), defaultElement, role error all manual
A-2.3 Every public part has at least one data-* attr OR explicit justification in component README (A2.3 exception: ...) warn all manual
A-2.4 Parts with non-trivial state declare states: [...] array warn interactive manual
A-2.5 Archetype ∈ ARCHETYPE_VOCABULARY (src/uix/morfo/types.ts). No invented archetypes. error all audit
A-2.6 Every part with focusable behavior has a tabindex or role that the browser focuses (no silent unfocusable interactive parts) warn interactive manual

A3 · Events — the part where current components leak

ID Rule Severity Applicability Enforcement
A-3.1 If interactive: events.length >= 1 error interactive audit
A-3.2 Every event has name, semantic.family, semantic.target (partRef) error all tool:check
A-3.3 semantic.family ∈ SEMA_FAMILIES (source: src/uix/sema/types.ts — do not copy the list) error all audit
A-3.4 semantic.verb ∈ SEMA_VERBS[family] error all audit
A-3.4b Per-event family.verb pairing is canonical (no verb borrowed from another family) warn all audit
A-3.5 semantic.sequence ∈ 'pre' | 'coincident' | 'post' declared explicitly warn interactive audit
A-3.6 Event name follows the {family}-{verb}[-{nuance}] pattern (validateMorfo throws otherwise) error interactive audit
A-3.7 Event/keyboard coverage: every distinct keyboard action that mutates state has a corresponding semantic event. Pure focus moves don't need an event. error interactive audit
A-3.8 If component supports value reset/clear: commit.reset or commit.discard event declared warn interactive manual
A-3.9 If component supports navigation steps (calendar, stepper, pagination): shift.navigate event declared with target part warn interactive manual
A-3.10 Intent is declared when family requires it per SEMA_FAMILY_POLICY (src/uix/sema/types.ts). target.partRef always set warn interactive audit

A4 · ARIA + keyboard

ID Rule Severity Applicability Enforcement
A-4.1 Provider part has aria-label or aria-labelledby declared with severity: 'recommended' warn interactive manual
A-4.2 If component has invalid/disabled/readonly/required state: matching aria-invalid/aria-disabled/aria-readonly/aria-required declared conditionally error interactive manual
A-4.3 If APG pattern requires specific keys (e.g., Grid: Arrow×4 + Home/End/PageUp/PageDown), all are declared in part keyboard warn interactive manual
A-4.4 No reinvented keys (Spacebar is ' '; Esc is 'Escape'; etc.) — must match KeyboardEvent.key values error interactive audit

B. Eidos wrapper

B1 · API shape (Option C disciplined)

ID Rule Severity Applicability Enforcement
E-1.1 Has {name}.svelte (root visual) + per-part files {name}-{part}.svelte error all audit
E-1.2 index.ts does explicit per-property assignment (X.Part = Part), not Object.assign(X, { ... }) error all audit
E-1.3 index.ts exports {Name} named + default {Name} error all audit
E-1.4 No exports of Provider, Base, Root, Parts, or Soma{Name}Provider error all audit
E-1.5 Imports Soma as import * as {Name} from '$soma/components/{kebab}' — namespace, not destructured warn all manual
E-1.6 Types: {Name}Props, {Name}Size, {Name}Variant (no EidosX* prefixes) error all manual
E-1.7 For single-part components, the default IS the component (Toggle, Switch, Icon) — no fake compound API error all manual

B2 · Files + structure

ID Rule Severity Applicability Enforcement
E-2.1 types.ts exports the public Props + size/variant/color unions error all audit
E-2.2 {name}.css exists and is wired: imported by the component's own wrapper (current, code-split pattern) OR from eidos/index.css (layout primitives + shared visuals like spin-field), OR a documented E-2.2 exception: in README (headless components with no visual recipe) error all audit
E-2.3 README.md exists with baseline (Air or "no baseline"), external comparison table, decisions, gaps error all audit
E-2.4 Every part declared in morfo (kind: 'public') has either a wrapper file or an explicit README note explaining why it's not exposed warn all manual
E-2.5 If the component has any direction-dependent behaviour or paint: public dir prop declared as dir?: Direction — the alias, never a hand-written 'ltr' | 'rtl' union (canon/direction-contract.md §1) error all manual

B3 · Wrapper internals

ID Rule Severity Applicability Enforcement
E-3.1 {name}.svelte renders Soma's {Name}.Provider (or equivalent) — does NOT mount Soma Trigger/Content directly error all manual
E-3.2 No $state re-declaration of bindable props from Soma (use $bindable proxy) warn all manual
E-3.3 Snippets receive children prop and don't shadow it with {#snippet children} in same scope error all manual
E-3.4 No data-_/CSS leaking from other layers: no data-soma-_, no --soma-_/--air-_ CSS variables error all manual
E-3.5 All visual props (size, variant, color, radius) map to data-{prop}="value" on the root for CSS to read. dir is exempt: it is native and stamped raw — data-dir is a legitimate extra (the resolved value, always present wherever a component opts into stamping it), never a substitute, since :dir() cannot see it warn all manual
E-3.6 If the component declares dir: the wrapper runs activeDir(() => dir, soma) at Provider.create(…) and the provider defaults once, in resolvedDir — it never re-implements the chain (no second defaulting, no prefs lookup, no DOM read). A further link (a submenu inheriting its parent menu) is composed at the call site, never added inside the provider error all manual
E-3.7 No silent internal write (catalogue invariant): every callback reporting writes to a bindable (onValueChange beside value) fires INSIDE that key's { get, set } setter, and the provider only writes — it never invokes the callback itself (that double-fires). A path that writes the bindable without notifying is a defect: bind: and the callback would see different histories. ONE named exception: a coalesced (debounced) notification, which must flush on clear/submit and emit from a single private method — component-guide.md §Callback conventions error components with a bindable + change callback manual
E-3.8 The wrapper builds its bag with bindProps<XOpts> (target-typed — the provider EXPORTS its Opts) or, when the whole bag is { id, ref }, with partOpts. No cast at the call site: a cast silences the whole bag warn all manual

C. Recipe CSS

C1 · State coverage

ID Rule Severity Applicability Enforcement
R-1.1 Has root selector [data-{component}] defining base layout/spacing, OR a documented R-1.1 exception: in README (foundation-riding components with no recipe of their own) error all audit
R-1.2 If morfo declares data-disabled on any part: [data-disabled] styled, OR a documented R-1.2 exception: in README (state owned by a composed primitive's recipe) error interactive audit
R-1.3 If morfo declares data-readonly: [data-readonly] styled warn interactive audit
R-1.4 If morfo declares data-invalid: [data-invalid] styled (using --color-risk-element or similar) warn interactive audit
R-1.5 All focusable parts show a focus treatment — :focus-visible, the field shell's :focus-within, a :has(…:focus…) rule, or the canonical [data-focused] state ring — OR a documented R-1.5 exception: in README (foundation archetype ring / shared layer / composed primitive / no focusable part) error interactive audit
R-1.6 Hover state defined for trigger-like archetypes (trigger, item, option, close, action) warn interactive manual
R-1.7 Disabled state has cursor: not-allowed OR documented exception in README warn interactive manual
R-1.8 If the recipe branches with :dir(…): the provider stamps the raw opts.dir.current as the native dir attribute — an unstamped assertion moves the maths and leaves the paint behind (canon/direction-contract.md §2) error all manual

C2 · Token discipline

ID Rule Severity Applicability Enforcement
R-2.1 No raw colors (hex/rgb/named). All colors come from var(--color-*) or var(--{component}-*). Applies to recipes/base.ts, archetypes.css, events.css and every component *.css — no exceptions. error all audit
R-2.2 No raw font-size in px/rem. Use var(--font-size-*) from Eidos recipe warn all audit (via R-2.7)
R-2.3 No magic numbers in spacing — use var(--space-*) or var(--{component}-*) warn all manual
R-2.4 Component-scoped tokens come from EidosConfig.recipes (base.css) — verify via ActiveEidos.listRecipes() warn all manual
R-2.5 No --eidos-* or --soma-* variable invented in component recipe error all audit
R-2.6 Every var(--color-X) referenced in recipes or component CSS is declared in generated/base.css. The theme contract (SurfaceColorRoles, ContentColorRoles, BorderColorRoles, FocusColorRoles + intent role maps) is the closed set; new tokens go through themes/base.ts + regen. error all audit
R-2.7 No literal typography in recipes (font-size, line-height, letter-spacing, font-weight raw values) — consume the foundation's type anchor via tokens. Escape valves: CSS keywords, numeric identities (0, 1), or a same-line /* literal: <reason> */ warn all audit

C3 · Color resolution

ID Rule Severity Applicability Enforcement
R-3.1 Recipe uses [data-color='X'] selectors only for the subset declared in component's README warn colored manual
R-3.2 No legacy color names (success, warning, danger, info) error all audit
R-3.3 Intent ↔ color resolution implemented: when component receives intent != 'neutral', data-color reflects the intent warn colored manual

C4 · Recipe Contract — transversal systems

Canon: canon/recipe-contract.md. These rules enforce that every recipe consumes the theming's transversal systems (state-layer, tokenized elevation, opacity token, logical axes, motion channel) instead of hand-rolling its own idiom. All R-4.x are error: R-4.1/4.2/4.3/4.4/4.6 graduated after the 2026-07-02 mechanical backfill; R-4.5 after the motion migration emptied its backlog (recipes consume the channel via preset stamp, signatures, or registered keyframes — the trigger-vs-materials doctrine is in the contract). The WIP tracks words / palabras / chronos are excluded. Escape valves: a same-line /* literal: <reason> */ (values), /* functional: <reason> */ (keyframes) or /* important: <reason> */ (!important).

ID Rule Severity Applicability Enforcement
R-4.1 No literal box-shadow — elevation goes through var(--shadow-*) / var(--depth-{plane}-*) (or the inset-ring pattern, which carries var()) error all audit
R-4.2 No literal fractional opacity outside @keyframes — disabled/muted states consume var(--opacity-*) error all audit
R-4.3 :hover backgrounds are the state-layer (var(--state-*)) or a palette token — no raw values, no hand-rolled color-mix(… currentColor …) error interactive audit
R-4.4 No physical-axis token keys (padding-x/-y, margin-x/-y) in lib/recipes/base.ts — logical axes (padding-inline/-block) are the canon error all audit
R-4.5 Local @keyframes require a /* functional: … */ annotation — perceptual signatures live in EidosConfig.motion, not in component CSS error all audit
R-4.6 No direct var(--scale-*) / var(--primitive-*) in component CSS — consume var(--color-{role}-{slot}) or recipe tokens error all audit
R-4.7 No !important without a same-line /* important: <reason> */ annotation — the declaration wins every cascade fight, so the reason lives where it happens error all audit
R-5.1 Every appearance declaration carries an act: a public var(--{c}-…) token, a /* literal: <reason> */ annotation, or an entry in the debt ledger (scripts/theming-census-debt.ts). New debt outside the ledger fails; a ledger entry whose knob is no longer debt is STALE and fails until the line is deleted error all audit
R-5.2 A recipe with themeable knobs declares them in lib/recipes/base.ts — unless it has nothing to declare (all system / structural) or every one of its knobs is registered in the debt ledger error all audit
R-5.3 Recipe token keys follow the naming grammar: the ink slot is fg (never color), and the interactive modifier goes IN FRONT (hover-bg) while size, orientation and context stay behind (control-height-md) — recipe-contract §1 error all audit

D. Demo page (web/routes/uix/components/{kebab}/+page.svelte)

D1 · Template compliance

ID Rule Severity Applicability Enforcement
D-1.1 Outer element is <div data-uix-canvas-inner> error all audit
D-1.2 Tab union matches the canonical v2 9-tab template ('live' | 'system' | 'motion' | 'sema' | 'services' | 'api' | 'morfo' | 'recipe' | 'a11y'); the v1 6-tab union is accepted only pending migration error all audit
D-1.3 Imports compileMorfo + the component's morfo error all audit
D-1.4 Imports getActiveUix if Sema tab has interactive Play buttons warn interactive manual
D-1.5 Has MutationObserver on data-event attribute, populating trace state error interactive audit
D-1.6 Has <div data-uix-stage> between header and tablist (live always rendered) error all audit
D-1.7 Has <div data-uix-stage-trace> with at least the trace strip error interactive audit

D2 · Header

ID Rule Severity Applicability Enforcement
D-2.1 Header has data-uix-eyebrow (Category · Name) error all audit
D-2.2 Header has <h1 data-uix-page-title> and <p data-uix-page-lede> (single paragraph summary) error all audit
D-2.3 Header has data-uix-page-meta with at minimum parts and events pills error all audit

D3 · Snippet parity (DEMO_AUTHORING §12)

ID Rule Severity Applicability Enforcement
D-3.1 eidosSnippet derived and rendered (the v2 template's single snippet; v1 demos may additionally carry somaSnippet) error all audit
D-3.2 Snippet code reflects current control values (not static placeholders) warn all manual
D-3.3 If live preview uses a schema/options/state, snippet declares the same warn all manual

D4 · Sema tab

ID Rule Severity Applicability Enforcement
D-4.1 Sema tab always rendered (even for 0-event components, with explicit empty state) error all manual
D-4.2 Sema tab has events table: name / family / verb / sequence / intent / play error interactive manual
D-4.3 Play buttons emit via uix.events.emit(...) onto a real DOM target inside the stage error interactive audit

D5 · Morfo tab (declarative contract surface)

ID Rule Severity Applicability Enforcement
D-5.1 Has header table: name / kebab / scope / apg / parts / events error all manual
D-5.2 Has parts overview table error all manual
D-5.3 For each part with data/aria/keyboard: per-part subsection rendered warn all manual
D-5.4 Events declaration table rendered error interactive manual

D6 · A11y tab + Recipe tab

ID Rule Severity Applicability Enforcement
D-6.1 A11y tab has keyboard table (from morfo) + ARIA contract table warn interactive manual
D-6.2 Recipe tab lists selectors with their layer source (morfo / eidos) warn all manual

D7 · Visible controls discipline (DEMO_AUTHORING §12.6)

ID Rule Severity Applicability Enforcement
D-7.1 Every control on the Live tab produces a visible change on the stage warn all manual
D-7.2 No demo-only data-* attrs hand-stamped to fake morfo selectors error all manual
D-7.3 Soma layer badge [soma] and Eidos layer badge [eidos] used to group control subsections warn all manual
D-7.4 Chip parity: every chip-group control (size, variant, color) enumerates the full union of the component's type — no truncated arrays. Theme defines 3 variants → all 3 are selectable. See DEMO_AUTHORING §6. error all audit
D-7.5 Size coverage: the chip array for size matches the component's declared union in recipe + types, 1:1 (form controls / text inputs / progress-meter / field-form expose xs..xl; nav controls expose xs..lg; passive panels keep sm..lg). warn all manual

E. Cross-layer integrity

ID Rule Severity Applicability Enforcement
X-1.1 npm run morfo:check PASS for this component error all tool:morfo:check
X-1.2 npm run perm:check PASS for this component if it's instrumented warn interactive tool:perm:check
X-1.3 eidos-lint-all.ts invalid count = 0 for this component error all tool:eidos-lint
X-1.4 npm run check does not produce errors in this component's files error all tool:check
X-1.5 Component's smoke route loads without console errors (SMOKE_SCOPE=/uix/components/{kebab} npm run smoke) error all tool:smoke
X-1.6 npm run rtl:check PASS (RTL-1 — logical inline anchor paired with a physical inline translate; RTL-2 — a :dir() rule that turns one logical face off and repaints the other, cancelling a mirror the property had already made). The run walks all of src/uix/eidos; there is no per-component scope error all tool:rtl:check

F. Documentation completeness (component README)

ID Rule Severity Applicability Enforcement
F-1.1 README has section "## Baseline" with Air comparison or explicit "no Air baseline" error all audit
F-1.2 README has section "## Comparativa" with at least 3 external references (Ark UI, Bits UI, Radix/shadcn, React Aria, MUI, or similar) error all audit
F-1.3 README has section "## Decisiones" with explicit choices made vs alternatives warn all audit
F-1.4 README has section "## Gaps" listing what's deferred / not implemented, each with disposition (implementar / diferir / descartar) error all audit
F-1.5 If component is passive (0 events): README explicitly justifies why (## Passive justification) error passive audit

G. Doctrinal canon (intent + color + sequence)

G1 · Subset declaration

ID Rule Severity Applicability Enforcement
G-1.1 README declares ## Subset listing which color values + which intent values the component accepts (per decisions/guia-semantica-historica.md §3) warn colored manual
G-1.2 Types restrict intent/color props to the declared subset via union types — not free-form string warn colored manual
G-1.3 Recipe CSS only matches [data-color='X'] for X in the declared subset warn colored manual

G2 · Sequence canon

ID Rule Severity Applicability Enforcement
G-2.1 Events that animate exit before structural commit use sequence: 'pre' (dismiss, close-cancel, etc.) warn interactive manual
G-2.2 Events that confirm a result use sequence: 'post' (commit-save, submit, etc.) warn interactive manual
G-2.3 Continuous progress events use sequence: 'coincident' (sustain.progress) warn passive manual

H. Severity summary table

A component is PASS when:

  • 0 errors across A–H
  • ≤3 warnings, each justified in README "## Audit exceptions"
  • All required scripts (X-1.x) pass

A component is NEEDS-WORK when 1-5 errors or >3 unjustified warnings.

A component is BROKEN when >5 errors OR any X-1.x script fails.


I. How to add a new rule

  1. Append to the appropriate section table with its Enforcement value.
  2. If Enforcement: audit: implement it in scripts/component-audit.ts with a check function that returns CheckResult, using the SAME rule ID the table declares (the report and this doc must grep-match).
  3. If Enforcement: manual / tool:X: no script change, but the value must be honest — declaring a rule here does not make it checked.
  4. Add the rule to the doctrinal source if it crosses a layer (architecture/active-architecture.md, CANON.md, or demo-authoring.md).
  5. Document the rationale at the top of the new rule's check function.

Rules should be doctrinally grounded (cite the source doc) and, when audit, machine-checkable (avoid pure aesthetic criteria — those go in the per-component README review). Keep this table and the script in sync: every audit rule ID exists in the script, and every rule ID the script emits exists here (npm run docs:check verifies both directions).

Powered by TurnKey Linux.