morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
import type { Morfo } from '../types';
|
morfo: enrich 65 components with aria + keyboard + focus contracts
Each morfo now declares what its provider actually emits (via agent-based
extraction from provider code). Before: parts + data only. After: parts +
data + ARIA per part + keyboard per part + focus policy for overlays.
Contract source: provider's `readonly props = $derived.by(...)` blocks,
classified into the v.* value kinds — literal, stateRef, partRef, propRef,
translationRef. Conditions extracted from ternary / guard patterns.
Focus policies added on overlay components: popover, drawer, alert-dialog,
dropdown-menu, context-menu, select, date-picker, date-range-picker,
time-picker, time-range-picker, color-picker. Context-menu uses
return:'previous' (no discrete trigger). Tooltip, link-preview, combobox,
command omit focus (no trap).
Tag-group Link variant: morfo-check tolerates sibling parts sharing a
physical element (Link IS Item in the DOM) via allPartAttrs cross-scope.
README.md added — developer guide: authoring a morfo step-by-step, the
v.* builders, severity rules, validation layers, polymorphism handling
via defaultElement + role, Sema alignment (data-last-action, transition
markers).
Verify:
npm run check — 5 errs, all pre-existing in sium/examples.
vitest run src/uix/morfo — 7/7 pass.
morfo:check — 64/66 pass (2 flaky Playwright navigation timeouts,
re-run clean when isolated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
import { v } from '../types';
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
|
soma/morfo: typed createAttrs + 6 audit batches + form hang fix + A36
## morfo: typed createAttrs
- `types.ts`: all array fields are now `readonly` so morfos can be authored
with `as const satisfies Morfo` without casts.
- `createAttrs` is a generic `<const M extends MorfoShape>` that infers the
emitted attr map from the morfo's literal shape. Typos like `attrs.trigerr`
are now compile errors; autocomplete lists every valid part.
- All 66 morfos migrated: `export const xMorfo = { ... } as const satisfies Morfo`.
- Two latent bugs the new typing caught: `dateRangeFieldAttrs.root` and
`timeRangeFieldAttrs.root` — legacy from the root→provider refactor, fixed
to `.provider`.
- Docs (morfo/README, COMPONENT_GUIDE) mandate `as const satisfies Morfo`;
the older `: Morfo` annotation widens literals and loses autocomplete.
## Audit batches 1–7 (soma-audit-2026-04-20)
Batch 1 — Keyboard a11y (A14/A12):
- Tabs: `isFallbackTabStop(el)` using `untrack` → first trigger gets
`tabindex=0` when no tab matches (WCAG 2.1.1 was failing).
- RatingGroup: `dir` prop + `getDirectionalKeys(dir, 'horizontal')` for RTL.
- Accordion P1-1/P1-6 dismissed: roving/labelledby already correct per APG.
Batch 2 — A33 reactive collections: Select `labelRegistry` and NavigationMenu
`triggerRefs` migrated to `SvelteMap`.
Batch 3 — A31 O(N²) fix: RadioGroup lifts `hasSelection` + `isFallbackTabStop`
to the provider with `untrack`-wrapped DOM walk. Per-item derivations drop
from O(N²) to O(1) per trigger.
Batch 4 — Morfo aria sync: toast / time-field / date-field / tabs /
tag-group / navigation-menu declare every aria the provider emits
(14 entries across 6 files).
Batch 5 — Accessible naming props: Tabs.List, ToggleGroup, TreeView,
NavigationMenu expose `aria-label?` / `aria-labelledby?` with explicit
"required unless labelledby is set" JSDoc.
Batch 6 — A30 cleanup: Checkbox `setLabelId` and 5 trigger-ref providers
(select/tooltip/link-preview/dropdown-menu/context-menu) migrated from
`$effect(() => parent.ref = opts.ref)` to the `Provider` base's
`onRefChange` callback (fires via attachRef inside `untrack`).
Batch 7 — A11 wrapper thinning: stepper/tree-view wrappers removed
`ctx.get()`, providers expose proxy derivations; time/date-range-field
inputs use `untrack(() => …)` for one-shot id registration; menubar
content's 40-line keyboard nav moved to `MenubarMenuProvider.onContentKeydown`;
color-picker area + channel-slider pointer state machines moved to the
providers with `onpointerdown/move/up` emitted via `props`.
form-auto-fields deferred with justification (reflective schema renderer,
not a state-holding wrapper).
## Form hang fix (A36)
`runValidate` entered the `isPromiseLike` branch even for sync schemas
because sium's Standard-Schema adapter declares `validate: async`. The
fallback `return { errors, issues }` read both reactive vars inside the
validation `$effect` while the `.then(...)` microtask wrote them
→ microtask-mediated loop, silent to Svelte's sync effect-depth guard.
Symptom: kitchen-sink hung the tab on mount with onChange / onBlur.
Fix: `return untrack(() => ({ errors, issues }))`. Regression locked by
two new 5s-timeout tests in `form-auto-fields.svelte.test.ts`.
Pattern codified as A36 in COMPONENT_GUIDE; CLAUDE.md count updated to
A1–A36 / 39 items.
## Verification
- `npm run check`: 0 errors in soma/morfo scope (sema demo pre-existing
errors untouched).
- `npx vitest run src/uix/soma src/uix/morfo`: all tests green
(94 + 7 = 101 tests, inc. 2 new form regression tests).
- `npm run smoke`: 65/65 routes.
- `npx tsx scripts/morfo-check.ts`: 66/66 morfos.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
export const drawerMorfo = {
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
name: 'Drawer',
|
|
|
|
|
kebab: 'drawer',
|
drawer: extend morfo with events + soft-migrate provider + exit tinting
The Drawer's "show" semantic was missing from the morfo, so close paths
went straight to handleClose() without the prewrite → emit → handler
cadence. User flagged this — drawer DOES have a present/dismiss
semantic, equivalent in surface to Dialog.
Morfo (`morfo/components/drawer.ts`):
- Added `events[]` mirroring Dialog: present, close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail
- Added `data-last-action` (saved/cancelled/dismissed/dismissed-outside/
failed) to Content's data so the prewrite has a declared slot
- scope: ['soma', 'sema'] now (sema participates via signal emit)
Eidos (`eidos/components/drawer.css`):
- Reserved a 3px transparent border on the LEADING edge of Content
per side (the edge facing the user, opposite to the slide-out
direction): right→border-left, left→border-right, top→border-bottom,
bottom→border-top
- Tint rules: combine [data-state='closed'] + [data-side] +
[data-last-action] so the right edge gets colored during the close
animation (saved → success, failed → danger, cancelled/dismissed →
neutral)
Soma (`soma/components/drawer/drawer-provider.svelte.ts`):
- DrawerProvider injects a MorfoRuntime (same pattern as Dialog soft-
migration). Registers content part. Wires events.{close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}
to handleClose().
- dismissWith(action) public method — same API as Dialog.dismissWith.
- Replaced 4 close paths with runtime.trigger():
Close.onclick → 'close-cancel'
Escape (modal) → 'close-dismiss'
Escape (non-modal) → 'close-dismiss'
Overlay.onclick → 'close-dismiss-outside'
Drag-to-dismiss (gesture) keeps direct handleClose() — the visible
slide-out IS the close, no 240ms hold makes sense.
- DrawerProvider class re-exported from $soma/components/drawer.
Demo (`test/eidos/drawer/+page.svelte`):
- New "Exit tinting by causa" section with Save/Cancel/Fail buttons
that call dismissWith() through a local _dismiss-button.svelte
(same pattern as Dialog's).
Verification (playwright):
save t=83ms lastAction=saved hold=240ms (commit family)
cancel t=119ms lastAction=cancelled hold=240ms (emerge)
fail t=127ms lastAction=failed hold=600ms (alert)
Smoke 68/68, morfo-check 66/66.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
scope: ['soma', 'sema'],
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/',
|
uix: rename morfo.translations → morfo.texts (idlangref discipline)
Morfo now declares text slots as absolute idlangrefs in `texts: Record<string, LangRef>`,
not literal multilingual records. Multilingual content moves out of morfo entirely
into per-component catalogs under `src/uix/langs/components/{kebab}.ts`, merged
globally by `ActiveUix` via the new `componentLangs` barrel. Morfo stays a pure
abstract surface; localization lives in its own layer.
- Type: `translations?: LangNode` → `texts?: Record<string, LangRef>` in
`src/uix/morfo/types.ts`. Doc and test fixtures updated.
- Catalog: new `src/uix/langs/` directory with `common.ts` (was `langs.ts`),
`components/` (one file per migrated morfo, plus a barrel), and an aggregate
`uixLangs`. `ActiveUix` registers both namespaces at boot.
- Registry: `registerMorfo` no longer carries a translations fingerprint and
no longer extends `ActiveLangs` dynamically. `connectMorfoTranslations` and
`MorfoTranslationTarget` deleted from the public morfo barrel.
- Validation: schema.ts drops the relative-translationRef-in-morfo.translations
check; catalog presence is enforced by the new `scripts/translations-check.ts`
(wired as `npm run translations:check`).
- Audit: `scripts/component-audit.ts` rule A-1.3 now validates `texts.label`
idlangref shape (`#?components.{kebab}.label|Fallback`) and explicitly errors
on any surviving legacy `translations:` field.
- Migration: `scripts/migrate-translations-to-texts.ts` is the one-off Node
script that produced the 42 morfo rewrites and 42 catalog files.
- Tests: `morfo/registry.test.ts` rewritten around the simpler contract;
`active-uix.svelte.test.ts` reflects boot-time catalog availability instead
of lazy per-morfo extension.
Verification: `npm run check` (0 errors), `npm run translations:check`
(0/0), `npm run morfo:check` (36/36 routed). The 4 pre-existing failures
in `test` are unrelated (date-picker types, css regen, range-calendar
clear, color contract).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
texts: {
|
|
|
|
|
label: '#?components.drawer.label|Drawer',
|
uix: rename morfo.translations → morfo.texts (idlangref discipline)
Morfo now declares text slots as absolute idlangrefs in `texts: Record<string, LangRef>`,
not literal multilingual records. Multilingual content moves out of morfo entirely
into per-component catalogs under `src/uix/langs/components/{kebab}.ts`, merged
globally by `ActiveUix` via the new `componentLangs` barrel. Morfo stays a pure
abstract surface; localization lives in its own layer.
- Type: `translations?: LangNode` → `texts?: Record<string, LangRef>` in
`src/uix/morfo/types.ts`. Doc and test fixtures updated.
- Catalog: new `src/uix/langs/` directory with `common.ts` (was `langs.ts`),
`components/` (one file per migrated morfo, plus a barrel), and an aggregate
`uixLangs`. `ActiveUix` registers both namespaces at boot.
- Registry: `registerMorfo` no longer carries a translations fingerprint and
no longer extends `ActiveLangs` dynamically. `connectMorfoTranslations` and
`MorfoTranslationTarget` deleted from the public morfo barrel.
- Validation: schema.ts drops the relative-translationRef-in-morfo.translations
check; catalog presence is enforced by the new `scripts/translations-check.ts`
(wired as `npm run translations:check`).
- Audit: `scripts/component-audit.ts` rule A-1.3 now validates `texts.label`
idlangref shape (`#?components.{kebab}.label|Fallback`) and explicitly errors
on any surviving legacy `translations:` field.
- Migration: `scripts/migrate-translations-to-texts.ts` is the one-off Node
script that produced the 42 morfo rewrites and 42 catalog files.
- Tests: `morfo/registry.test.ts` rewritten around the simpler contract;
`active-uix.svelte.test.ts` reflects boot-time catalog availability instead
of lazy per-morfo extension.
Verification: `npm run check` (0 errors), `npm run translations:check`
(0/0), `npm run morfo:check` (36/36 routed). The 4 pre-existing failures
in `test` are unrelated (date-picker types, css regen, range-calendar
clear, color contract).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
trigger: '#?components.drawer.trigger|Open drawer'
|
|
|
|
|
},
|
morfo: enrich 65 components with aria + keyboard + focus contracts
Each morfo now declares what its provider actually emits (via agent-based
extraction from provider code). Before: parts + data only. After: parts +
data + ARIA per part + keyboard per part + focus policy for overlays.
Contract source: provider's `readonly props = $derived.by(...)` blocks,
classified into the v.* value kinds — literal, stateRef, partRef, propRef,
translationRef. Conditions extracted from ternary / guard patterns.
Focus policies added on overlay components: popover, drawer, alert-dialog,
dropdown-menu, context-menu, select, date-picker, date-range-picker,
time-picker, time-range-picker, color-picker. Context-menu uses
return:'previous' (no discrete trigger). Tooltip, link-preview, combobox,
command omit focus (no trap).
Tag-group Link variant: morfo-check tolerates sibling parts sharing a
physical element (Link IS Item in the DOM) via allPartAttrs cross-scope.
README.md added — developer guide: authoring a morfo step-by-step, the
v.* builders, severity rules, validation layers, polymorphism handling
via defaultElement + role, Sema alignment (data-last-action, transition
markers).
Verify:
npm run check — 5 errs, all pre-existing in sium/examples.
vitest run src/uix/morfo — 7/7 pass.
morfo:check — 64/66 pass (2 flaky Playwright navigation timeouts,
re-run clean when isolated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
|
drawer: extend morfo with events + soft-migrate provider + exit tinting
The Drawer's "show" semantic was missing from the morfo, so close paths
went straight to handleClose() without the prewrite → emit → handler
cadence. User flagged this — drawer DOES have a present/dismiss
semantic, equivalent in surface to Dialog.
Morfo (`morfo/components/drawer.ts`):
- Added `events[]` mirroring Dialog: present, close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail
- Added `data-last-action` (saved/cancelled/dismissed/dismissed-outside/
failed) to Content's data so the prewrite has a declared slot
- scope: ['soma', 'sema'] now (sema participates via signal emit)
Eidos (`eidos/components/drawer.css`):
- Reserved a 3px transparent border on the LEADING edge of Content
per side (the edge facing the user, opposite to the slide-out
direction): right→border-left, left→border-right, top→border-bottom,
bottom→border-top
- Tint rules: combine [data-state='closed'] + [data-side] +
[data-last-action] so the right edge gets colored during the close
animation (saved → success, failed → danger, cancelled/dismissed →
neutral)
Soma (`soma/components/drawer/drawer-provider.svelte.ts`):
- DrawerProvider injects a MorfoRuntime (same pattern as Dialog soft-
migration). Registers content part. Wires events.{close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}
to handleClose().
- dismissWith(action) public method — same API as Dialog.dismissWith.
- Replaced 4 close paths with runtime.trigger():
Close.onclick → 'close-cancel'
Escape (modal) → 'close-dismiss'
Escape (non-modal) → 'close-dismiss'
Overlay.onclick → 'close-dismiss-outside'
Drag-to-dismiss (gesture) keeps direct handleClose() — the visible
slide-out IS the close, no 240ms hold makes sense.
- DrawerProvider class re-exported from $soma/components/drawer.
Demo (`test/eidos/drawer/+page.svelte`):
- New "Exit tinting by causa" section with Save/Cancel/Fail buttons
that call dismissWith() through a local _dismiss-button.svelte
(same pattern as Dialog's).
Verification (playwright):
save t=83ms lastAction=saved hold=240ms (commit family)
cancel t=119ms lastAction=cancelled hold=240ms (emerge)
fail t=127ms lastAction=failed hold=600ms (alert)
Smoke 68/68, morfo-check 66/66.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
events: [
|
|
|
|
|
{
|
|
|
|
|
name: 'present',
|
sema/morfo/eidos: align with GUIA_IMPLEMENTACION_SEMAUIX (canonical guide)
The new doctrinal guide at src/docs/GUIA_IMPLEMENTACION_SEMAUIX.md
supersedes src/docs/sema-implementation-guide.md and locks the
canonical vocabulary, event shape and color tokens. This commit aligns
the codebase to that canon in three concrete moves.
Phase 1 — sema/verbs.ts vocabulary:
- Restructured from flat array to family-keyed Record per the guide.
- Moved select / toggle / acknowledge → commit (these fix state, not
contact). Removed edit from handle (it's shift.enter-mode now).
- Added new verbs per the canon: contact (tap, focus, trigger,
release), commit (complete, restore, expire, set, remove, reorder),
signal (remind), handle (rotate, scroll, reorder), shift (route,
step, return, context), sustain (start, loading, waiting, syncing,
processing, streaming, pending, retrying, end).
- validateEventName now recognises both naming shapes:
{verb}-{variant} (head=verb) and {family}-{verb} (head=family).
- New helper familyForVerb. Tests rewritten — 16/16 green.
Phase 2 — morfo event shape:
- target moved from event-level into event.semantic. Adds optional
semantic.verb (advisory, validated against SEMA_VERBS[family]) and
optional semantic.sequence ('pre' | 'coincident' | 'post').
- compile.ts and schema.ts updated. The 22 events across toggle,
toast, popover, drawer, dialog refactored to the new shape with
explicit verb + sequence.
- Polymorphic capability (allowedFamilies + defaultSemantic) and
a11ySemantic deferred — no current consumer needs them; will land
when the first concrete usage appears.
Phase 5 — color tokens (doctrinal 8):
- themes/base/light.css and dark.css rewritten to expose primary,
secondary, neutral, affirm, fulfill, risk, threat, loss. Renames
success→fulfill, warning→risk, danger→threat (same hex). New
primitives for secondary (slate-blue), affirm (teal-mint, low
activation), loss (deep violet-grave, posterior). info palette
deleted entirely — per guide, "info no es intent, es signal.announce
+ neutral".
- _static.css legacy compatibility aliases (--color-bg, --color-success,
--color-error, --color-accent, ...) deleted. Clean cut, no
transition. Consumers reference doctrinal palettes directly.
- 29 consumers (token files + recipes) migrated:
- All --*-color-info-* declarations dropped.
- Component palettes extended with secondary / affirm / loss.
- switch + checkbox recipes updated to use [data-color='affirm'|
'risk'|'threat'|'secondary'] (was 'success'|'warning'|'danger'|
'info').
- toast tokens + recipe renamed to affirm/fulfill/risk/threat/loss
(was success/error/warning/info).
- meter, progress, mark, badge, button tokens renamed.
- Contracts (contracts/contract.css + contracts/components/*)
updated.
- focus-ring-color-error in themes now points to --color-risk-border
(validation errors are signal.warn + risk per doctrina, not threat).
Phase 4 (partial):
- src/docs/sema-implementation-guide.md deleted (superseded by the
canonical guide).
Tests: 217/217 green across uix. npm run check holds at the 39-error
baseline (all pre-existing in unrelated test files; none introduced).
Pending in follow-up commits:
- Update active_architecture.md, sema/README.md, morfo/README.md,
CLAUDE.md to reflect the canon.
- Switch component migration to the eidos wrapper pattern.
- Persistence field (untilAction / untilFix / stateBound) and
holds-by-intent — defer until the first signal.warn / signal.alert
consumer appears.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
semantic: {
|
|
|
|
|
family: 'emerge',
|
|
|
|
|
verb: 'present',
|
|
|
|
|
target: v.partRef('content'),
|
|
|
|
|
sequence: 'pre'
|
|
|
|
|
},
|
drawer: extend morfo with events + soft-migrate provider + exit tinting
The Drawer's "show" semantic was missing from the morfo, so close paths
went straight to handleClose() without the prewrite → emit → handler
cadence. User flagged this — drawer DOES have a present/dismiss
semantic, equivalent in surface to Dialog.
Morfo (`morfo/components/drawer.ts`):
- Added `events[]` mirroring Dialog: present, close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail
- Added `data-last-action` (saved/cancelled/dismissed/dismissed-outside/
failed) to Content's data so the prewrite has a declared slot
- scope: ['soma', 'sema'] now (sema participates via signal emit)
Eidos (`eidos/components/drawer.css`):
- Reserved a 3px transparent border on the LEADING edge of Content
per side (the edge facing the user, opposite to the slide-out
direction): right→border-left, left→border-right, top→border-bottom,
bottom→border-top
- Tint rules: combine [data-state='closed'] + [data-side] +
[data-last-action] so the right edge gets colored during the close
animation (saved → success, failed → danger, cancelled/dismissed →
neutral)
Soma (`soma/components/drawer/drawer-provider.svelte.ts`):
- DrawerProvider injects a MorfoRuntime (same pattern as Dialog soft-
migration). Registers content part. Wires events.{close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}
to handleClose().
- dismissWith(action) public method — same API as Dialog.dismissWith.
- Replaced 4 close paths with runtime.trigger():
Close.onclick → 'close-cancel'
Escape (modal) → 'close-dismiss'
Escape (non-modal) → 'close-dismiss'
Overlay.onclick → 'close-dismiss-outside'
Drag-to-dismiss (gesture) keeps direct handleClose() — the visible
slide-out IS the close, no 240ms hold makes sense.
- DrawerProvider class re-exported from $soma/components/drawer.
Demo (`test/eidos/drawer/+page.svelte`):
- New "Exit tinting by causa" section with Save/Cancel/Fail buttons
that call dismissWith() through a local _dismiss-button.svelte
(same pattern as Dialog's).
Verification (playwright):
save t=83ms lastAction=saved hold=240ms (commit family)
cancel t=119ms lastAction=cancelled hold=240ms (emerge)
fail t=127ms lastAction=failed hold=600ms (alert)
Smoke 68/68, morfo-check 66/66.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
commits: {
|
|
|
|
|
part: v.partRef('content'),
|
|
|
|
|
attr: 'data-state',
|
|
|
|
|
value: 'open'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'close-save',
|
sema/morfo/eidos: align with GUIA_IMPLEMENTACION_SEMAUIX (canonical guide)
The new doctrinal guide at src/docs/GUIA_IMPLEMENTACION_SEMAUIX.md
supersedes src/docs/sema-implementation-guide.md and locks the
canonical vocabulary, event shape and color tokens. This commit aligns
the codebase to that canon in three concrete moves.
Phase 1 — sema/verbs.ts vocabulary:
- Restructured from flat array to family-keyed Record per the guide.
- Moved select / toggle / acknowledge → commit (these fix state, not
contact). Removed edit from handle (it's shift.enter-mode now).
- Added new verbs per the canon: contact (tap, focus, trigger,
release), commit (complete, restore, expire, set, remove, reorder),
signal (remind), handle (rotate, scroll, reorder), shift (route,
step, return, context), sustain (start, loading, waiting, syncing,
processing, streaming, pending, retrying, end).
- validateEventName now recognises both naming shapes:
{verb}-{variant} (head=verb) and {family}-{verb} (head=family).
- New helper familyForVerb. Tests rewritten — 16/16 green.
Phase 2 — morfo event shape:
- target moved from event-level into event.semantic. Adds optional
semantic.verb (advisory, validated against SEMA_VERBS[family]) and
optional semantic.sequence ('pre' | 'coincident' | 'post').
- compile.ts and schema.ts updated. The 22 events across toggle,
toast, popover, drawer, dialog refactored to the new shape with
explicit verb + sequence.
- Polymorphic capability (allowedFamilies + defaultSemantic) and
a11ySemantic deferred — no current consumer needs them; will land
when the first concrete usage appears.
Phase 5 — color tokens (doctrinal 8):
- themes/base/light.css and dark.css rewritten to expose primary,
secondary, neutral, affirm, fulfill, risk, threat, loss. Renames
success→fulfill, warning→risk, danger→threat (same hex). New
primitives for secondary (slate-blue), affirm (teal-mint, low
activation), loss (deep violet-grave, posterior). info palette
deleted entirely — per guide, "info no es intent, es signal.announce
+ neutral".
- _static.css legacy compatibility aliases (--color-bg, --color-success,
--color-error, --color-accent, ...) deleted. Clean cut, no
transition. Consumers reference doctrinal palettes directly.
- 29 consumers (token files + recipes) migrated:
- All --*-color-info-* declarations dropped.
- Component palettes extended with secondary / affirm / loss.
- switch + checkbox recipes updated to use [data-color='affirm'|
'risk'|'threat'|'secondary'] (was 'success'|'warning'|'danger'|
'info').
- toast tokens + recipe renamed to affirm/fulfill/risk/threat/loss
(was success/error/warning/info).
- meter, progress, mark, badge, button tokens renamed.
- Contracts (contracts/contract.css + contracts/components/*)
updated.
- focus-ring-color-error in themes now points to --color-risk-border
(validation errors are signal.warn + risk per doctrina, not threat).
Phase 4 (partial):
- src/docs/sema-implementation-guide.md deleted (superseded by the
canonical guide).
Tests: 217/217 green across uix. npm run check holds at the 39-error
baseline (all pre-existing in unrelated test files; none introduced).
Pending in follow-up commits:
- Update active_architecture.md, sema/README.md, morfo/README.md,
CLAUDE.md to reflect the canon.
- Switch component migration to the eidos wrapper pattern.
- Persistence field (untilAction / untilFix / stateBound) and
holds-by-intent — defer until the first signal.warn / signal.alert
consumer appears.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
semantic: {
|
|
|
|
|
family: 'commit',
|
|
|
|
|
verb: 'save',
|
|
|
|
|
target: v.partRef('content'),
|
|
|
|
|
sequence: 'post',
|
|
|
|
|
intent: 'fulfill'
|
|
|
|
|
},
|
drawer: extend morfo with events + soft-migrate provider + exit tinting
The Drawer's "show" semantic was missing from the morfo, so close paths
went straight to handleClose() without the prewrite → emit → handler
cadence. User flagged this — drawer DOES have a present/dismiss
semantic, equivalent in surface to Dialog.
Morfo (`morfo/components/drawer.ts`):
- Added `events[]` mirroring Dialog: present, close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail
- Added `data-last-action` (saved/cancelled/dismissed/dismissed-outside/
failed) to Content's data so the prewrite has a declared slot
- scope: ['soma', 'sema'] now (sema participates via signal emit)
Eidos (`eidos/components/drawer.css`):
- Reserved a 3px transparent border on the LEADING edge of Content
per side (the edge facing the user, opposite to the slide-out
direction): right→border-left, left→border-right, top→border-bottom,
bottom→border-top
- Tint rules: combine [data-state='closed'] + [data-side] +
[data-last-action] so the right edge gets colored during the close
animation (saved → success, failed → danger, cancelled/dismissed →
neutral)
Soma (`soma/components/drawer/drawer-provider.svelte.ts`):
- DrawerProvider injects a MorfoRuntime (same pattern as Dialog soft-
migration). Registers content part. Wires events.{close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}
to handleClose().
- dismissWith(action) public method — same API as Dialog.dismissWith.
- Replaced 4 close paths with runtime.trigger():
Close.onclick → 'close-cancel'
Escape (modal) → 'close-dismiss'
Escape (non-modal) → 'close-dismiss'
Overlay.onclick → 'close-dismiss-outside'
Drag-to-dismiss (gesture) keeps direct handleClose() — the visible
slide-out IS the close, no 240ms hold makes sense.
- DrawerProvider class re-exported from $soma/components/drawer.
Demo (`test/eidos/drawer/+page.svelte`):
- New "Exit tinting by causa" section with Save/Cancel/Fail buttons
that call dismissWith() through a local _dismiss-button.svelte
(same pattern as Dialog's).
Verification (playwright):
save t=83ms lastAction=saved hold=240ms (commit family)
cancel t=119ms lastAction=cancelled hold=240ms (emerge)
fail t=127ms lastAction=failed hold=600ms (alert)
Smoke 68/68, morfo-check 66/66.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
regime: 'lock',
|
|
|
|
|
prewrite: [{ part: v.partRef('content'), attr: 'data-last-action', value: 'saved' }],
|
|
|
|
|
commits: {
|
|
|
|
|
part: v.partRef('content'),
|
|
|
|
|
attr: 'data-state',
|
|
|
|
|
value: 'closed'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'close-cancel',
|
sema/morfo/eidos: align with GUIA_IMPLEMENTACION_SEMAUIX (canonical guide)
The new doctrinal guide at src/docs/GUIA_IMPLEMENTACION_SEMAUIX.md
supersedes src/docs/sema-implementation-guide.md and locks the
canonical vocabulary, event shape and color tokens. This commit aligns
the codebase to that canon in three concrete moves.
Phase 1 — sema/verbs.ts vocabulary:
- Restructured from flat array to family-keyed Record per the guide.
- Moved select / toggle / acknowledge → commit (these fix state, not
contact). Removed edit from handle (it's shift.enter-mode now).
- Added new verbs per the canon: contact (tap, focus, trigger,
release), commit (complete, restore, expire, set, remove, reorder),
signal (remind), handle (rotate, scroll, reorder), shift (route,
step, return, context), sustain (start, loading, waiting, syncing,
processing, streaming, pending, retrying, end).
- validateEventName now recognises both naming shapes:
{verb}-{variant} (head=verb) and {family}-{verb} (head=family).
- New helper familyForVerb. Tests rewritten — 16/16 green.
Phase 2 — morfo event shape:
- target moved from event-level into event.semantic. Adds optional
semantic.verb (advisory, validated against SEMA_VERBS[family]) and
optional semantic.sequence ('pre' | 'coincident' | 'post').
- compile.ts and schema.ts updated. The 22 events across toggle,
toast, popover, drawer, dialog refactored to the new shape with
explicit verb + sequence.
- Polymorphic capability (allowedFamilies + defaultSemantic) and
a11ySemantic deferred — no current consumer needs them; will land
when the first concrete usage appears.
Phase 5 — color tokens (doctrinal 8):
- themes/base/light.css and dark.css rewritten to expose primary,
secondary, neutral, affirm, fulfill, risk, threat, loss. Renames
success→fulfill, warning→risk, danger→threat (same hex). New
primitives for secondary (slate-blue), affirm (teal-mint, low
activation), loss (deep violet-grave, posterior). info palette
deleted entirely — per guide, "info no es intent, es signal.announce
+ neutral".
- _static.css legacy compatibility aliases (--color-bg, --color-success,
--color-error, --color-accent, ...) deleted. Clean cut, no
transition. Consumers reference doctrinal palettes directly.
- 29 consumers (token files + recipes) migrated:
- All --*-color-info-* declarations dropped.
- Component palettes extended with secondary / affirm / loss.
- switch + checkbox recipes updated to use [data-color='affirm'|
'risk'|'threat'|'secondary'] (was 'success'|'warning'|'danger'|
'info').
- toast tokens + recipe renamed to affirm/fulfill/risk/threat/loss
(was success/error/warning/info).
- meter, progress, mark, badge, button tokens renamed.
- Contracts (contracts/contract.css + contracts/components/*)
updated.
- focus-ring-color-error in themes now points to --color-risk-border
(validation errors are signal.warn + risk per doctrina, not threat).
Phase 4 (partial):
- src/docs/sema-implementation-guide.md deleted (superseded by the
canonical guide).
Tests: 217/217 green across uix. npm run check holds at the 39-error
baseline (all pre-existing in unrelated test files; none introduced).
Pending in follow-up commits:
- Update active_architecture.md, sema/README.md, morfo/README.md,
CLAUDE.md to reflect the canon.
- Switch component migration to the eidos wrapper pattern.
- Persistence field (untilAction / untilFix / stateBound) and
holds-by-intent — defer until the first signal.warn / signal.alert
consumer appears.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
semantic: {
|
|
|
|
|
family: 'emerge',
|
|
|
|
|
verb: 'close',
|
|
|
|
|
target: v.partRef('content'),
|
|
|
|
|
sequence: 'pre'
|
|
|
|
|
},
|
drawer: extend morfo with events + soft-migrate provider + exit tinting
The Drawer's "show" semantic was missing from the morfo, so close paths
went straight to handleClose() without the prewrite → emit → handler
cadence. User flagged this — drawer DOES have a present/dismiss
semantic, equivalent in surface to Dialog.
Morfo (`morfo/components/drawer.ts`):
- Added `events[]` mirroring Dialog: present, close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail
- Added `data-last-action` (saved/cancelled/dismissed/dismissed-outside/
failed) to Content's data so the prewrite has a declared slot
- scope: ['soma', 'sema'] now (sema participates via signal emit)
Eidos (`eidos/components/drawer.css`):
- Reserved a 3px transparent border on the LEADING edge of Content
per side (the edge facing the user, opposite to the slide-out
direction): right→border-left, left→border-right, top→border-bottom,
bottom→border-top
- Tint rules: combine [data-state='closed'] + [data-side] +
[data-last-action] so the right edge gets colored during the close
animation (saved → success, failed → danger, cancelled/dismissed →
neutral)
Soma (`soma/components/drawer/drawer-provider.svelte.ts`):
- DrawerProvider injects a MorfoRuntime (same pattern as Dialog soft-
migration). Registers content part. Wires events.{close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}
to handleClose().
- dismissWith(action) public method — same API as Dialog.dismissWith.
- Replaced 4 close paths with runtime.trigger():
Close.onclick → 'close-cancel'
Escape (modal) → 'close-dismiss'
Escape (non-modal) → 'close-dismiss'
Overlay.onclick → 'close-dismiss-outside'
Drag-to-dismiss (gesture) keeps direct handleClose() — the visible
slide-out IS the close, no 240ms hold makes sense.
- DrawerProvider class re-exported from $soma/components/drawer.
Demo (`test/eidos/drawer/+page.svelte`):
- New "Exit tinting by causa" section with Save/Cancel/Fail buttons
that call dismissWith() through a local _dismiss-button.svelte
(same pattern as Dialog's).
Verification (playwright):
save t=83ms lastAction=saved hold=240ms (commit family)
cancel t=119ms lastAction=cancelled hold=240ms (emerge)
fail t=127ms lastAction=failed hold=600ms (alert)
Smoke 68/68, morfo-check 66/66.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
regime: 'lock',
|
|
|
|
|
prewrite: [{ part: v.partRef('content'), attr: 'data-last-action', value: 'cancelled' }],
|
|
|
|
|
commits: {
|
|
|
|
|
part: v.partRef('content'),
|
|
|
|
|
attr: 'data-state',
|
|
|
|
|
value: 'closed'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'close-dismiss',
|
sema/morfo/eidos: align with GUIA_IMPLEMENTACION_SEMAUIX (canonical guide)
The new doctrinal guide at src/docs/GUIA_IMPLEMENTACION_SEMAUIX.md
supersedes src/docs/sema-implementation-guide.md and locks the
canonical vocabulary, event shape and color tokens. This commit aligns
the codebase to that canon in three concrete moves.
Phase 1 — sema/verbs.ts vocabulary:
- Restructured from flat array to family-keyed Record per the guide.
- Moved select / toggle / acknowledge → commit (these fix state, not
contact). Removed edit from handle (it's shift.enter-mode now).
- Added new verbs per the canon: contact (tap, focus, trigger,
release), commit (complete, restore, expire, set, remove, reorder),
signal (remind), handle (rotate, scroll, reorder), shift (route,
step, return, context), sustain (start, loading, waiting, syncing,
processing, streaming, pending, retrying, end).
- validateEventName now recognises both naming shapes:
{verb}-{variant} (head=verb) and {family}-{verb} (head=family).
- New helper familyForVerb. Tests rewritten — 16/16 green.
Phase 2 — morfo event shape:
- target moved from event-level into event.semantic. Adds optional
semantic.verb (advisory, validated against SEMA_VERBS[family]) and
optional semantic.sequence ('pre' | 'coincident' | 'post').
- compile.ts and schema.ts updated. The 22 events across toggle,
toast, popover, drawer, dialog refactored to the new shape with
explicit verb + sequence.
- Polymorphic capability (allowedFamilies + defaultSemantic) and
a11ySemantic deferred — no current consumer needs them; will land
when the first concrete usage appears.
Phase 5 — color tokens (doctrinal 8):
- themes/base/light.css and dark.css rewritten to expose primary,
secondary, neutral, affirm, fulfill, risk, threat, loss. Renames
success→fulfill, warning→risk, danger→threat (same hex). New
primitives for secondary (slate-blue), affirm (teal-mint, low
activation), loss (deep violet-grave, posterior). info palette
deleted entirely — per guide, "info no es intent, es signal.announce
+ neutral".
- _static.css legacy compatibility aliases (--color-bg, --color-success,
--color-error, --color-accent, ...) deleted. Clean cut, no
transition. Consumers reference doctrinal palettes directly.
- 29 consumers (token files + recipes) migrated:
- All --*-color-info-* declarations dropped.
- Component palettes extended with secondary / affirm / loss.
- switch + checkbox recipes updated to use [data-color='affirm'|
'risk'|'threat'|'secondary'] (was 'success'|'warning'|'danger'|
'info').
- toast tokens + recipe renamed to affirm/fulfill/risk/threat/loss
(was success/error/warning/info).
- meter, progress, mark, badge, button tokens renamed.
- Contracts (contracts/contract.css + contracts/components/*)
updated.
- focus-ring-color-error in themes now points to --color-risk-border
(validation errors are signal.warn + risk per doctrina, not threat).
Phase 4 (partial):
- src/docs/sema-implementation-guide.md deleted (superseded by the
canonical guide).
Tests: 217/217 green across uix. npm run check holds at the 39-error
baseline (all pre-existing in unrelated test files; none introduced).
Pending in follow-up commits:
- Update active_architecture.md, sema/README.md, morfo/README.md,
CLAUDE.md to reflect the canon.
- Switch component migration to the eidos wrapper pattern.
- Persistence field (untilAction / untilFix / stateBound) and
holds-by-intent — defer until the first signal.warn / signal.alert
consumer appears.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
semantic: {
|
|
|
|
|
family: 'emerge',
|
|
|
|
|
verb: 'dismiss',
|
|
|
|
|
target: v.partRef('content'),
|
|
|
|
|
sequence: 'pre'
|
|
|
|
|
},
|
drawer: extend morfo with events + soft-migrate provider + exit tinting
The Drawer's "show" semantic was missing from the morfo, so close paths
went straight to handleClose() without the prewrite → emit → handler
cadence. User flagged this — drawer DOES have a present/dismiss
semantic, equivalent in surface to Dialog.
Morfo (`morfo/components/drawer.ts`):
- Added `events[]` mirroring Dialog: present, close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail
- Added `data-last-action` (saved/cancelled/dismissed/dismissed-outside/
failed) to Content's data so the prewrite has a declared slot
- scope: ['soma', 'sema'] now (sema participates via signal emit)
Eidos (`eidos/components/drawer.css`):
- Reserved a 3px transparent border on the LEADING edge of Content
per side (the edge facing the user, opposite to the slide-out
direction): right→border-left, left→border-right, top→border-bottom,
bottom→border-top
- Tint rules: combine [data-state='closed'] + [data-side] +
[data-last-action] so the right edge gets colored during the close
animation (saved → success, failed → danger, cancelled/dismissed →
neutral)
Soma (`soma/components/drawer/drawer-provider.svelte.ts`):
- DrawerProvider injects a MorfoRuntime (same pattern as Dialog soft-
migration). Registers content part. Wires events.{close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}
to handleClose().
- dismissWith(action) public method — same API as Dialog.dismissWith.
- Replaced 4 close paths with runtime.trigger():
Close.onclick → 'close-cancel'
Escape (modal) → 'close-dismiss'
Escape (non-modal) → 'close-dismiss'
Overlay.onclick → 'close-dismiss-outside'
Drag-to-dismiss (gesture) keeps direct handleClose() — the visible
slide-out IS the close, no 240ms hold makes sense.
- DrawerProvider class re-exported from $soma/components/drawer.
Demo (`test/eidos/drawer/+page.svelte`):
- New "Exit tinting by causa" section with Save/Cancel/Fail buttons
that call dismissWith() through a local _dismiss-button.svelte
(same pattern as Dialog's).
Verification (playwright):
save t=83ms lastAction=saved hold=240ms (commit family)
cancel t=119ms lastAction=cancelled hold=240ms (emerge)
fail t=127ms lastAction=failed hold=600ms (alert)
Smoke 68/68, morfo-check 66/66.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
regime: 'lock',
|
|
|
|
|
prewrite: [{ part: v.partRef('content'), attr: 'data-last-action', value: 'dismissed' }],
|
|
|
|
|
commits: {
|
|
|
|
|
part: v.partRef('content'),
|
|
|
|
|
attr: 'data-state',
|
|
|
|
|
value: 'closed'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'close-dismiss-outside',
|
sema/morfo/eidos: align with GUIA_IMPLEMENTACION_SEMAUIX (canonical guide)
The new doctrinal guide at src/docs/GUIA_IMPLEMENTACION_SEMAUIX.md
supersedes src/docs/sema-implementation-guide.md and locks the
canonical vocabulary, event shape and color tokens. This commit aligns
the codebase to that canon in three concrete moves.
Phase 1 — sema/verbs.ts vocabulary:
- Restructured from flat array to family-keyed Record per the guide.
- Moved select / toggle / acknowledge → commit (these fix state, not
contact). Removed edit from handle (it's shift.enter-mode now).
- Added new verbs per the canon: contact (tap, focus, trigger,
release), commit (complete, restore, expire, set, remove, reorder),
signal (remind), handle (rotate, scroll, reorder), shift (route,
step, return, context), sustain (start, loading, waiting, syncing,
processing, streaming, pending, retrying, end).
- validateEventName now recognises both naming shapes:
{verb}-{variant} (head=verb) and {family}-{verb} (head=family).
- New helper familyForVerb. Tests rewritten — 16/16 green.
Phase 2 — morfo event shape:
- target moved from event-level into event.semantic. Adds optional
semantic.verb (advisory, validated against SEMA_VERBS[family]) and
optional semantic.sequence ('pre' | 'coincident' | 'post').
- compile.ts and schema.ts updated. The 22 events across toggle,
toast, popover, drawer, dialog refactored to the new shape with
explicit verb + sequence.
- Polymorphic capability (allowedFamilies + defaultSemantic) and
a11ySemantic deferred — no current consumer needs them; will land
when the first concrete usage appears.
Phase 5 — color tokens (doctrinal 8):
- themes/base/light.css and dark.css rewritten to expose primary,
secondary, neutral, affirm, fulfill, risk, threat, loss. Renames
success→fulfill, warning→risk, danger→threat (same hex). New
primitives for secondary (slate-blue), affirm (teal-mint, low
activation), loss (deep violet-grave, posterior). info palette
deleted entirely — per guide, "info no es intent, es signal.announce
+ neutral".
- _static.css legacy compatibility aliases (--color-bg, --color-success,
--color-error, --color-accent, ...) deleted. Clean cut, no
transition. Consumers reference doctrinal palettes directly.
- 29 consumers (token files + recipes) migrated:
- All --*-color-info-* declarations dropped.
- Component palettes extended with secondary / affirm / loss.
- switch + checkbox recipes updated to use [data-color='affirm'|
'risk'|'threat'|'secondary'] (was 'success'|'warning'|'danger'|
'info').
- toast tokens + recipe renamed to affirm/fulfill/risk/threat/loss
(was success/error/warning/info).
- meter, progress, mark, badge, button tokens renamed.
- Contracts (contracts/contract.css + contracts/components/*)
updated.
- focus-ring-color-error in themes now points to --color-risk-border
(validation errors are signal.warn + risk per doctrina, not threat).
Phase 4 (partial):
- src/docs/sema-implementation-guide.md deleted (superseded by the
canonical guide).
Tests: 217/217 green across uix. npm run check holds at the 39-error
baseline (all pre-existing in unrelated test files; none introduced).
Pending in follow-up commits:
- Update active_architecture.md, sema/README.md, morfo/README.md,
CLAUDE.md to reflect the canon.
- Switch component migration to the eidos wrapper pattern.
- Persistence field (untilAction / untilFix / stateBound) and
holds-by-intent — defer until the first signal.warn / signal.alert
consumer appears.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
semantic: {
|
|
|
|
|
family: 'emerge',
|
|
|
|
|
verb: 'dismiss',
|
|
|
|
|
target: v.partRef('content'),
|
|
|
|
|
sequence: 'pre'
|
|
|
|
|
},
|
drawer: extend morfo with events + soft-migrate provider + exit tinting
The Drawer's "show" semantic was missing from the morfo, so close paths
went straight to handleClose() without the prewrite → emit → handler
cadence. User flagged this — drawer DOES have a present/dismiss
semantic, equivalent in surface to Dialog.
Morfo (`morfo/components/drawer.ts`):
- Added `events[]` mirroring Dialog: present, close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail
- Added `data-last-action` (saved/cancelled/dismissed/dismissed-outside/
failed) to Content's data so the prewrite has a declared slot
- scope: ['soma', 'sema'] now (sema participates via signal emit)
Eidos (`eidos/components/drawer.css`):
- Reserved a 3px transparent border on the LEADING edge of Content
per side (the edge facing the user, opposite to the slide-out
direction): right→border-left, left→border-right, top→border-bottom,
bottom→border-top
- Tint rules: combine [data-state='closed'] + [data-side] +
[data-last-action] so the right edge gets colored during the close
animation (saved → success, failed → danger, cancelled/dismissed →
neutral)
Soma (`soma/components/drawer/drawer-provider.svelte.ts`):
- DrawerProvider injects a MorfoRuntime (same pattern as Dialog soft-
migration). Registers content part. Wires events.{close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}
to handleClose().
- dismissWith(action) public method — same API as Dialog.dismissWith.
- Replaced 4 close paths with runtime.trigger():
Close.onclick → 'close-cancel'
Escape (modal) → 'close-dismiss'
Escape (non-modal) → 'close-dismiss'
Overlay.onclick → 'close-dismiss-outside'
Drag-to-dismiss (gesture) keeps direct handleClose() — the visible
slide-out IS the close, no 240ms hold makes sense.
- DrawerProvider class re-exported from $soma/components/drawer.
Demo (`test/eidos/drawer/+page.svelte`):
- New "Exit tinting by causa" section with Save/Cancel/Fail buttons
that call dismissWith() through a local _dismiss-button.svelte
(same pattern as Dialog's).
Verification (playwright):
save t=83ms lastAction=saved hold=240ms (commit family)
cancel t=119ms lastAction=cancelled hold=240ms (emerge)
fail t=127ms lastAction=failed hold=600ms (alert)
Smoke 68/68, morfo-check 66/66.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
regime: 'lock',
|
|
|
|
|
prewrite: [
|
|
|
|
|
{ part: v.partRef('content'), attr: 'data-last-action', value: 'dismissed-outside' }
|
|
|
|
|
],
|
|
|
|
|
commits: {
|
|
|
|
|
part: v.partRef('content'),
|
|
|
|
|
attr: 'data-state',
|
|
|
|
|
value: 'closed'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'close-after-fail',
|
sema/morfo/eidos: align with GUIA_IMPLEMENTACION_SEMAUIX (canonical guide)
The new doctrinal guide at src/docs/GUIA_IMPLEMENTACION_SEMAUIX.md
supersedes src/docs/sema-implementation-guide.md and locks the
canonical vocabulary, event shape and color tokens. This commit aligns
the codebase to that canon in three concrete moves.
Phase 1 — sema/verbs.ts vocabulary:
- Restructured from flat array to family-keyed Record per the guide.
- Moved select / toggle / acknowledge → commit (these fix state, not
contact). Removed edit from handle (it's shift.enter-mode now).
- Added new verbs per the canon: contact (tap, focus, trigger,
release), commit (complete, restore, expire, set, remove, reorder),
signal (remind), handle (rotate, scroll, reorder), shift (route,
step, return, context), sustain (start, loading, waiting, syncing,
processing, streaming, pending, retrying, end).
- validateEventName now recognises both naming shapes:
{verb}-{variant} (head=verb) and {family}-{verb} (head=family).
- New helper familyForVerb. Tests rewritten — 16/16 green.
Phase 2 — morfo event shape:
- target moved from event-level into event.semantic. Adds optional
semantic.verb (advisory, validated against SEMA_VERBS[family]) and
optional semantic.sequence ('pre' | 'coincident' | 'post').
- compile.ts and schema.ts updated. The 22 events across toggle,
toast, popover, drawer, dialog refactored to the new shape with
explicit verb + sequence.
- Polymorphic capability (allowedFamilies + defaultSemantic) and
a11ySemantic deferred — no current consumer needs them; will land
when the first concrete usage appears.
Phase 5 — color tokens (doctrinal 8):
- themes/base/light.css and dark.css rewritten to expose primary,
secondary, neutral, affirm, fulfill, risk, threat, loss. Renames
success→fulfill, warning→risk, danger→threat (same hex). New
primitives for secondary (slate-blue), affirm (teal-mint, low
activation), loss (deep violet-grave, posterior). info palette
deleted entirely — per guide, "info no es intent, es signal.announce
+ neutral".
- _static.css legacy compatibility aliases (--color-bg, --color-success,
--color-error, --color-accent, ...) deleted. Clean cut, no
transition. Consumers reference doctrinal palettes directly.
- 29 consumers (token files + recipes) migrated:
- All --*-color-info-* declarations dropped.
- Component palettes extended with secondary / affirm / loss.
- switch + checkbox recipes updated to use [data-color='affirm'|
'risk'|'threat'|'secondary'] (was 'success'|'warning'|'danger'|
'info').
- toast tokens + recipe renamed to affirm/fulfill/risk/threat/loss
(was success/error/warning/info).
- meter, progress, mark, badge, button tokens renamed.
- Contracts (contracts/contract.css + contracts/components/*)
updated.
- focus-ring-color-error in themes now points to --color-risk-border
(validation errors are signal.warn + risk per doctrina, not threat).
Phase 4 (partial):
- src/docs/sema-implementation-guide.md deleted (superseded by the
canonical guide).
Tests: 217/217 green across uix. npm run check holds at the 39-error
baseline (all pre-existing in unrelated test files; none introduced).
Pending in follow-up commits:
- Update active_architecture.md, sema/README.md, morfo/README.md,
CLAUDE.md to reflect the canon.
- Switch component migration to the eidos wrapper pattern.
- Persistence field (untilAction / untilFix / stateBound) and
holds-by-intent — defer until the first signal.warn / signal.alert
consumer appears.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
semantic: {
|
|
|
|
|
family: 'signal',
|
|
|
|
|
verb: 'alert',
|
|
|
|
|
target: v.partRef('content'),
|
|
|
|
|
sequence: 'pre',
|
|
|
|
|
intent: 'threat'
|
|
|
|
|
},
|
drawer: extend morfo with events + soft-migrate provider + exit tinting
The Drawer's "show" semantic was missing from the morfo, so close paths
went straight to handleClose() without the prewrite → emit → handler
cadence. User flagged this — drawer DOES have a present/dismiss
semantic, equivalent in surface to Dialog.
Morfo (`morfo/components/drawer.ts`):
- Added `events[]` mirroring Dialog: present, close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail
- Added `data-last-action` (saved/cancelled/dismissed/dismissed-outside/
failed) to Content's data so the prewrite has a declared slot
- scope: ['soma', 'sema'] now (sema participates via signal emit)
Eidos (`eidos/components/drawer.css`):
- Reserved a 3px transparent border on the LEADING edge of Content
per side (the edge facing the user, opposite to the slide-out
direction): right→border-left, left→border-right, top→border-bottom,
bottom→border-top
- Tint rules: combine [data-state='closed'] + [data-side] +
[data-last-action] so the right edge gets colored during the close
animation (saved → success, failed → danger, cancelled/dismissed →
neutral)
Soma (`soma/components/drawer/drawer-provider.svelte.ts`):
- DrawerProvider injects a MorfoRuntime (same pattern as Dialog soft-
migration). Registers content part. Wires events.{close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}
to handleClose().
- dismissWith(action) public method — same API as Dialog.dismissWith.
- Replaced 4 close paths with runtime.trigger():
Close.onclick → 'close-cancel'
Escape (modal) → 'close-dismiss'
Escape (non-modal) → 'close-dismiss'
Overlay.onclick → 'close-dismiss-outside'
Drag-to-dismiss (gesture) keeps direct handleClose() — the visible
slide-out IS the close, no 240ms hold makes sense.
- DrawerProvider class re-exported from $soma/components/drawer.
Demo (`test/eidos/drawer/+page.svelte`):
- New "Exit tinting by causa" section with Save/Cancel/Fail buttons
that call dismissWith() through a local _dismiss-button.svelte
(same pattern as Dialog's).
Verification (playwright):
save t=83ms lastAction=saved hold=240ms (commit family)
cancel t=119ms lastAction=cancelled hold=240ms (emerge)
fail t=127ms lastAction=failed hold=600ms (alert)
Smoke 68/68, morfo-check 66/66.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
regime: 'lock',
|
|
|
|
|
prewrite: [{ part: v.partRef('content'), attr: 'data-last-action', value: 'failed' }],
|
|
|
|
|
commits: {
|
|
|
|
|
part: v.partRef('content'),
|
|
|
|
|
attr: 'data-state',
|
|
|
|
|
value: 'closed'
|
|
|
|
|
}
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
},
|
|
|
|
|
// ── Direct manipulation (handle family, canon §1.3) ──────────────────
|
|
|
|
|
// The drawer is a manipulable object; its drag interaction is a
|
|
|
|
|
// first-class semantic surface. Channels per-event let the consumer
|
|
|
|
|
// silence the perceptual signal where it would be noisy (e.g. drop
|
|
|
|
|
// haptic from drag-progress) without the morfo deciding for them.
|
|
|
|
|
{
|
|
|
|
|
name: 'drag-start',
|
|
|
|
|
semantic: {
|
|
|
|
|
family: 'handle',
|
|
|
|
|
verb: 'pick',
|
|
|
|
|
target: v.partRef('content'),
|
|
|
|
|
sequence: 'pre'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'drag-progress',
|
|
|
|
|
semantic: {
|
|
|
|
|
family: 'handle',
|
|
|
|
|
verb: 'drag',
|
|
|
|
|
target: v.partRef('content'),
|
|
|
|
|
// The signal is concurrent with the visible motion of the
|
|
|
|
|
// finger. Soma throttles the trigger to rAF so we don't queue
|
|
|
|
|
// hundreds of holds per second.
|
|
|
|
|
sequence: 'coincident'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'drag-end',
|
|
|
|
|
semantic: {
|
|
|
|
|
family: 'handle',
|
|
|
|
|
verb: 'drop',
|
|
|
|
|
target: v.partRef('content'),
|
|
|
|
|
sequence: 'post'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'resize',
|
|
|
|
|
semantic: {
|
|
|
|
|
family: 'handle',
|
|
|
|
|
verb: 'resize',
|
|
|
|
|
target: v.partRef('content'),
|
|
|
|
|
sequence: 'post'
|
|
|
|
|
}
|
drawer: extend morfo with events + soft-migrate provider + exit tinting
The Drawer's "show" semantic was missing from the morfo, so close paths
went straight to handleClose() without the prewrite → emit → handler
cadence. User flagged this — drawer DOES have a present/dismiss
semantic, equivalent in surface to Dialog.
Morfo (`morfo/components/drawer.ts`):
- Added `events[]` mirroring Dialog: present, close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail
- Added `data-last-action` (saved/cancelled/dismissed/dismissed-outside/
failed) to Content's data so the prewrite has a declared slot
- scope: ['soma', 'sema'] now (sema participates via signal emit)
Eidos (`eidos/components/drawer.css`):
- Reserved a 3px transparent border on the LEADING edge of Content
per side (the edge facing the user, opposite to the slide-out
direction): right→border-left, left→border-right, top→border-bottom,
bottom→border-top
- Tint rules: combine [data-state='closed'] + [data-side] +
[data-last-action] so the right edge gets colored during the close
animation (saved → success, failed → danger, cancelled/dismissed →
neutral)
Soma (`soma/components/drawer/drawer-provider.svelte.ts`):
- DrawerProvider injects a MorfoRuntime (same pattern as Dialog soft-
migration). Registers content part. Wires events.{close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}
to handleClose().
- dismissWith(action) public method — same API as Dialog.dismissWith.
- Replaced 4 close paths with runtime.trigger():
Close.onclick → 'close-cancel'
Escape (modal) → 'close-dismiss'
Escape (non-modal) → 'close-dismiss'
Overlay.onclick → 'close-dismiss-outside'
Drag-to-dismiss (gesture) keeps direct handleClose() — the visible
slide-out IS the close, no 240ms hold makes sense.
- DrawerProvider class re-exported from $soma/components/drawer.
Demo (`test/eidos/drawer/+page.svelte`):
- New "Exit tinting by causa" section with Save/Cancel/Fail buttons
that call dismissWith() through a local _dismiss-button.svelte
(same pattern as Dialog's).
Verification (playwright):
save t=83ms lastAction=saved hold=240ms (commit family)
cancel t=119ms lastAction=cancelled hold=240ms (emerge)
fail t=127ms lastAction=failed hold=600ms (alert)
Smoke 68/68, morfo-check 66/66.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
|
morfo: enrich 65 components with aria + keyboard + focus contracts
Each morfo now declares what its provider actually emits (via agent-based
extraction from provider code). Before: parts + data only. After: parts +
data + ARIA per part + keyboard per part + focus policy for overlays.
Contract source: provider's `readonly props = $derived.by(...)` blocks,
classified into the v.* value kinds — literal, stateRef, partRef, propRef,
translationRef. Conditions extracted from ternary / guard patterns.
Focus policies added on overlay components: popover, drawer, alert-dialog,
dropdown-menu, context-menu, select, date-picker, date-range-picker,
time-picker, time-range-picker, color-picker. Context-menu uses
return:'previous' (no discrete trigger). Tooltip, link-preview, combobox,
command omit focus (no trap).
Tag-group Link variant: morfo-check tolerates sibling parts sharing a
physical element (Link IS Item in the DOM) via allPartAttrs cross-scope.
README.md added — developer guide: authoring a morfo step-by-step, the
v.* builders, severity rules, validation layers, polymorphism handling
via defaultElement + role, Sema alignment (data-last-action, transition
markers).
Verify:
npm run check — 5 errs, all pre-existing in sium/examples.
vitest run src/uix/morfo — 7/7 pass.
morfo:check — 64/66 pass (2 flaky Playwright navigation timeouts,
re-run clean when isolated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
focus: {
|
|
|
|
|
initial: 'first-focusable',
|
|
|
|
|
trap: true,
|
|
|
|
|
return: 'trigger',
|
|
|
|
|
restore: true
|
|
|
|
|
},
|
|
|
|
|
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
parts: [
|
|
|
|
|
{
|
|
|
|
|
name: 'Provider',
|
|
|
|
|
kebab: 'provider',
|
morfo: catalog `archetype` across 65 components (~265 part declarations)
Bulk classification pass that gives every part a cross-component archetype
where one applies. Done with `scripts/catalog-archetypes.ts` — a one-shot
mechanical mapping based on a kebab → archetype table, plus three manual
overrides for components where Provider IS the interactive element.
Coverage
- 440 part declarations examined.
- ~265 received an archetype (provider, trigger, content, overlay, viewport,
item, indicator, thumb, track, label, title, description, close, action,
header, image, fallback, arrow, separator, group, input, segment).
- ~175 left without one — genuinely component-specific (Slider.Range,
PinInput.Segment internals, color-picker channel-slider, etc.).
Manual overrides (Provider IS the trigger)
- Toggle, Switch, Checkbox: their `Provider` part is a `<button>` that
the user actually interacts with, not a root container. Archetype set to
`'trigger'` instead of the default `'provider'` so eidos can style them
alongside Dialog.Trigger / Popover.Trigger / etc.
Script (scripts/catalog-archetypes.ts)
- Walks every `*.ts` under `src/uix/morfo/components/`.
- Inserts `archetype: '...'` directly after the matching `kebab:` line when
a mapping exists.
- Idempotent — skips parts that already declare an archetype.
- Indent-aware — only operates on part kebabs (≥2 tabs), never the morfo
root kebab. Bug discovered + fixed mid-pass when radio-group's root
kebab `'radio-group'` matched the `'radio-group' → 'group'` alias and
inserted at the wrong level (cleaned up by hand).
- Reports per-file counts + the list of unmapped kebabs at the end so
future passes can extend the mapping.
Tests
- Updated `omits data-archetype when not declared` test to use a tiny
inline morfo (Toggle's Provider now has archetype 'trigger', so it can't
be the "no archetype" fixture anymore).
Verification
- 22/22 runtime tests, full focused suite green.
- 66/66 morfo-check (every component still validates against its DOM —
archetype is additive, doesn't change emitted attrs apart from the new
`data-archetype="..."` which morfo-check doesn't enforce yet).
- 68/68 smoke routes.
- svelte-check unchanged at 155.
6 months ago
|
|
|
archetype: 'provider',
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
kind: 'virtual',
|
|
|
|
|
defaultElement: 'none',
|
|
|
|
|
optional: false,
|
|
|
|
|
states: ['open', 'closed'],
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
data: [
|
|
|
|
|
{ attr: 'data-state', values: ['open', 'closed'], value: v.stateRef('open') },
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* Drawer presentation mode. Three variants:
|
|
|
|
|
* 'overlay' — modal: backdrop + focus trap + dismissible (default)
|
|
|
|
|
* 'inline' — non-modal: no backdrop, no focus trap, dismissible
|
|
|
|
|
* via Escape; sits over content without blocking it
|
|
|
|
|
* 'persistent' — always-visible: no Trigger, no close-* events,
|
|
|
|
|
* part of the layout (sidebar pattern)
|
|
|
|
|
*
|
|
|
|
|
* Eidos consumes `data-variant` to pick positioning (fixed vs
|
|
|
|
|
* static) and overlay rendering. Soma derives `modal: boolean`
|
|
|
|
|
* internally as `variant === 'overlay'` for the aria-modal
|
|
|
|
|
* condition + focus/scroll-lock behavior.
|
|
|
|
|
*/
|
|
|
|
|
attr: 'data-variant',
|
|
|
|
|
values: ['overlay', 'inline', 'persistent'],
|
|
|
|
|
value: v.propRef('variant')
|
|
|
|
|
}
|
|
|
|
|
],
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
aria: []
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Trigger',
|
|
|
|
|
kebab: 'trigger',
|
morfo: catalog `archetype` across 65 components (~265 part declarations)
Bulk classification pass that gives every part a cross-component archetype
where one applies. Done with `scripts/catalog-archetypes.ts` — a one-shot
mechanical mapping based on a kebab → archetype table, plus three manual
overrides for components where Provider IS the interactive element.
Coverage
- 440 part declarations examined.
- ~265 received an archetype (provider, trigger, content, overlay, viewport,
item, indicator, thumb, track, label, title, description, close, action,
header, image, fallback, arrow, separator, group, input, segment).
- ~175 left without one — genuinely component-specific (Slider.Range,
PinInput.Segment internals, color-picker channel-slider, etc.).
Manual overrides (Provider IS the trigger)
- Toggle, Switch, Checkbox: their `Provider` part is a `<button>` that
the user actually interacts with, not a root container. Archetype set to
`'trigger'` instead of the default `'provider'` so eidos can style them
alongside Dialog.Trigger / Popover.Trigger / etc.
Script (scripts/catalog-archetypes.ts)
- Walks every `*.ts` under `src/uix/morfo/components/`.
- Inserts `archetype: '...'` directly after the matching `kebab:` line when
a mapping exists.
- Idempotent — skips parts that already declare an archetype.
- Indent-aware — only operates on part kebabs (≥2 tabs), never the morfo
root kebab. Bug discovered + fixed mid-pass when radio-group's root
kebab `'radio-group'` matched the `'radio-group' → 'group'` alias and
inserted at the wrong level (cleaned up by hand).
- Reports per-file counts + the list of unmapped kebabs at the end so
future passes can extend the mapping.
Tests
- Updated `omits data-archetype when not declared` test to use a tiny
inline morfo (Toggle's Provider now has archetype 'trigger', so it can't
be the "no archetype" fixture anymore).
Verification
- 22/22 runtime tests, full focused suite green.
- 66/66 morfo-check (every component still validates against its DOM —
archetype is additive, doesn't change emitted attrs apart from the new
`data-archetype="..."` which morfo-check doesn't enforce yet).
- 68/68 smoke routes.
- svelte-check unchanged at 155.
6 months ago
|
|
|
archetype: 'trigger',
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'button',
|
morfo: enrich 65 components with aria + keyboard + focus contracts
Each morfo now declares what its provider actually emits (via agent-based
extraction from provider code). Before: parts + data only. After: parts +
data + ARIA per part + keyboard per part + focus policy for overlays.
Contract source: provider's `readonly props = $derived.by(...)` blocks,
classified into the v.* value kinds — literal, stateRef, partRef, propRef,
translationRef. Conditions extracted from ternary / guard patterns.
Focus policies added on overlay components: popover, drawer, alert-dialog,
dropdown-menu, context-menu, select, date-picker, date-range-picker,
time-picker, time-range-picker, color-picker. Context-menu uses
return:'previous' (no discrete trigger). Tooltip, link-preview, combobox,
command omit focus (no trap).
Tag-group Link variant: morfo-check tolerates sibling parts sharing a
physical element (Link IS Item in the DOM) via allPartAttrs cross-scope.
README.md added — developer guide: authoring a morfo step-by-step, the
v.* builders, severity rules, validation layers, polymorphism handling
via defaultElement + role, Sema alignment (data-last-action, transition
markers).
Verify:
npm run check — 5 errs, all pre-existing in sium/examples.
vitest run src/uix/morfo — 7/7 pass.
morfo:check — 64/66 pass (2 flaky Playwright navigation timeouts,
re-run clean when isolated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
role: 'button',
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
optional: false,
|
|
|
|
|
states: ['open', 'closed'],
|
|
|
|
|
data: [
|
|
|
|
|
{ attr: 'data-state', values: ['open', 'closed'] },
|
|
|
|
|
{ attr: 'data-disabled', severity: 'optional' }
|
|
|
|
|
],
|
morfo: enrich 65 components with aria + keyboard + focus contracts
Each morfo now declares what its provider actually emits (via agent-based
extraction from provider code). Before: parts + data only. After: parts +
data + ARIA per part + keyboard per part + focus policy for overlays.
Contract source: provider's `readonly props = $derived.by(...)` blocks,
classified into the v.* value kinds — literal, stateRef, partRef, propRef,
translationRef. Conditions extracted from ternary / guard patterns.
Focus policies added on overlay components: popover, drawer, alert-dialog,
dropdown-menu, context-menu, select, date-picker, date-range-picker,
time-picker, time-range-picker, color-picker. Context-menu uses
return:'previous' (no discrete trigger). Tooltip, link-preview, combobox,
command omit focus (no trap).
Tag-group Link variant: morfo-check tolerates sibling parts sharing a
physical element (Link IS Item in the DOM) via allPartAttrs cross-scope.
README.md added — developer guide: authoring a morfo step-by-step, the
v.* builders, severity rules, validation layers, polymorphism handling
via defaultElement + role, Sema alignment (data-last-action, transition
markers).
Verify:
npm run check — 5 errs, all pre-existing in sium/examples.
vitest run src/uix/morfo — 7/7 pass.
morfo:check — 64/66 pass (2 flaky Playwright navigation timeouts,
re-run clean when isolated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
aria: [
|
|
|
|
|
{ attr: 'type', value: v.literal('button') },
|
|
|
|
|
{ attr: 'aria-haspopup', value: v.literal('dialog') },
|
|
|
|
|
{ attr: 'aria-expanded', value: v.stateRef('open') },
|
|
|
|
|
{
|
|
|
|
|
attr: 'aria-controls',
|
|
|
|
|
value: v.partRef('content'),
|
|
|
|
|
condition: { when: 'part-present', part: 'content' },
|
|
|
|
|
severity: 'recommended'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
attr: 'aria-label',
|
|
|
|
|
value: v.translationRef('#?components.drawer.trigger|Open drawer'),
|
|
|
|
|
severity: 'recommended'
|
|
|
|
|
}
|
|
|
|
|
]
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Content',
|
|
|
|
|
kebab: 'content',
|
morfo: catalog `archetype` across 65 components (~265 part declarations)
Bulk classification pass that gives every part a cross-component archetype
where one applies. Done with `scripts/catalog-archetypes.ts` — a one-shot
mechanical mapping based on a kebab → archetype table, plus three manual
overrides for components where Provider IS the interactive element.
Coverage
- 440 part declarations examined.
- ~265 received an archetype (provider, trigger, content, overlay, viewport,
item, indicator, thumb, track, label, title, description, close, action,
header, image, fallback, arrow, separator, group, input, segment).
- ~175 left without one — genuinely component-specific (Slider.Range,
PinInput.Segment internals, color-picker channel-slider, etc.).
Manual overrides (Provider IS the trigger)
- Toggle, Switch, Checkbox: their `Provider` part is a `<button>` that
the user actually interacts with, not a root container. Archetype set to
`'trigger'` instead of the default `'provider'` so eidos can style them
alongside Dialog.Trigger / Popover.Trigger / etc.
Script (scripts/catalog-archetypes.ts)
- Walks every `*.ts` under `src/uix/morfo/components/`.
- Inserts `archetype: '...'` directly after the matching `kebab:` line when
a mapping exists.
- Idempotent — skips parts that already declare an archetype.
- Indent-aware — only operates on part kebabs (≥2 tabs), never the morfo
root kebab. Bug discovered + fixed mid-pass when radio-group's root
kebab `'radio-group'` matched the `'radio-group' → 'group'` alias and
inserted at the wrong level (cleaned up by hand).
- Reports per-file counts + the list of unmapped kebabs at the end so
future passes can extend the mapping.
Tests
- Updated `omits data-archetype when not declared` test to use a tiny
inline morfo (Toggle's Provider now has archetype 'trigger', so it can't
be the "no archetype" fixture anymore).
Verification
- 22/22 runtime tests, full focused suite green.
- 66/66 morfo-check (every component still validates against its DOM —
archetype is additive, doesn't change emitted attrs apart from the new
`data-archetype="..."` which morfo-check doesn't enforce yet).
- 68/68 smoke routes.
- svelte-check unchanged at 155.
6 months ago
|
|
|
archetype: 'content',
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'div',
|
morfo: enrich 65 components with aria + keyboard + focus contracts
Each morfo now declares what its provider actually emits (via agent-based
extraction from provider code). Before: parts + data only. After: parts +
data + ARIA per part + keyboard per part + focus policy for overlays.
Contract source: provider's `readonly props = $derived.by(...)` blocks,
classified into the v.* value kinds — literal, stateRef, partRef, propRef,
translationRef. Conditions extracted from ternary / guard patterns.
Focus policies added on overlay components: popover, drawer, alert-dialog,
dropdown-menu, context-menu, select, date-picker, date-range-picker,
time-picker, time-range-picker, color-picker. Context-menu uses
return:'previous' (no discrete trigger). Tooltip, link-preview, combobox,
command omit focus (no trap).
Tag-group Link variant: morfo-check tolerates sibling parts sharing a
physical element (Link IS Item in the DOM) via allPartAttrs cross-scope.
README.md added — developer guide: authoring a morfo step-by-step, the
v.* builders, severity rules, validation layers, polymorphism handling
via defaultElement + role, Sema alignment (data-last-action, transition
markers).
Verify:
npm run check — 5 errs, all pre-existing in sium/examples.
vitest run src/uix/morfo — 7/7 pass.
morfo:check — 64/66 pass (2 flaky Playwright navigation timeouts,
re-run clean when isolated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
role: 'dialog',
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
optional: false,
|
|
|
|
|
states: ['open', 'closed'],
|
|
|
|
|
data: [
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
{ attr: 'data-state', values: ['open', 'closed'], value: v.stateRef('open') },
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
{ attr: 'data-side', values: ['top', 'bottom', 'left', 'right'] },
|
drawer: extend morfo with events + soft-migrate provider + exit tinting
The Drawer's "show" semantic was missing from the morfo, so close paths
went straight to handleClose() without the prewrite → emit → handler
cadence. User flagged this — drawer DOES have a present/dismiss
semantic, equivalent in surface to Dialog.
Morfo (`morfo/components/drawer.ts`):
- Added `events[]` mirroring Dialog: present, close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail
- Added `data-last-action` (saved/cancelled/dismissed/dismissed-outside/
failed) to Content's data so the prewrite has a declared slot
- scope: ['soma', 'sema'] now (sema participates via signal emit)
Eidos (`eidos/components/drawer.css`):
- Reserved a 3px transparent border on the LEADING edge of Content
per side (the edge facing the user, opposite to the slide-out
direction): right→border-left, left→border-right, top→border-bottom,
bottom→border-top
- Tint rules: combine [data-state='closed'] + [data-side] +
[data-last-action] so the right edge gets colored during the close
animation (saved → success, failed → danger, cancelled/dismissed →
neutral)
Soma (`soma/components/drawer/drawer-provider.svelte.ts`):
- DrawerProvider injects a MorfoRuntime (same pattern as Dialog soft-
migration). Registers content part. Wires events.{close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}
to handleClose().
- dismissWith(action) public method — same API as Dialog.dismissWith.
- Replaced 4 close paths with runtime.trigger():
Close.onclick → 'close-cancel'
Escape (modal) → 'close-dismiss'
Escape (non-modal) → 'close-dismiss'
Overlay.onclick → 'close-dismiss-outside'
Drag-to-dismiss (gesture) keeps direct handleClose() — the visible
slide-out IS the close, no 240ms hold makes sense.
- DrawerProvider class re-exported from $soma/components/drawer.
Demo (`test/eidos/drawer/+page.svelte`):
- New "Exit tinting by causa" section with Save/Cancel/Fail buttons
that call dismissWith() through a local _dismiss-button.svelte
(same pattern as Dialog's).
Verification (playwright):
save t=83ms lastAction=saved hold=240ms (commit family)
cancel t=119ms lastAction=cancelled hold=240ms (emerge)
fail t=127ms lastAction=failed hold=600ms (alert)
Smoke 68/68, morfo-check 66/66.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
{ attr: 'data-dragging', severity: 'optional' },
|
|
|
|
|
{ attr: 'data-nested', severity: 'optional' },
|
|
|
|
|
{ attr: 'data-nested-open', severity: 'optional' },
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
{
|
|
|
|
|
// Mirrors `data-variant` on the provider so recipe selectors
|
|
|
|
|
// can scope by variant directly on Content (`[data-drawer-content]
|
|
|
|
|
// [data-variant='inline']`) without a descendant combinator.
|
|
|
|
|
attr: 'data-variant',
|
|
|
|
|
values: ['overlay', 'inline', 'persistent'],
|
|
|
|
|
value: v.propRef('variant')
|
|
|
|
|
},
|
drawer: extend morfo with events + soft-migrate provider + exit tinting
The Drawer's "show" semantic was missing from the morfo, so close paths
went straight to handleClose() without the prewrite → emit → handler
cadence. User flagged this — drawer DOES have a present/dismiss
semantic, equivalent in surface to Dialog.
Morfo (`morfo/components/drawer.ts`):
- Added `events[]` mirroring Dialog: present, close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail
- Added `data-last-action` (saved/cancelled/dismissed/dismissed-outside/
failed) to Content's data so the prewrite has a declared slot
- scope: ['soma', 'sema'] now (sema participates via signal emit)
Eidos (`eidos/components/drawer.css`):
- Reserved a 3px transparent border on the LEADING edge of Content
per side (the edge facing the user, opposite to the slide-out
direction): right→border-left, left→border-right, top→border-bottom,
bottom→border-top
- Tint rules: combine [data-state='closed'] + [data-side] +
[data-last-action] so the right edge gets colored during the close
animation (saved → success, failed → danger, cancelled/dismissed →
neutral)
Soma (`soma/components/drawer/drawer-provider.svelte.ts`):
- DrawerProvider injects a MorfoRuntime (same pattern as Dialog soft-
migration). Registers content part. Wires events.{close-save,
close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}
to handleClose().
- dismissWith(action) public method — same API as Dialog.dismissWith.
- Replaced 4 close paths with runtime.trigger():
Close.onclick → 'close-cancel'
Escape (modal) → 'close-dismiss'
Escape (non-modal) → 'close-dismiss'
Overlay.onclick → 'close-dismiss-outside'
Drag-to-dismiss (gesture) keeps direct handleClose() — the visible
slide-out IS the close, no 240ms hold makes sense.
- DrawerProvider class re-exported from $soma/components/drawer.
Demo (`test/eidos/drawer/+page.svelte`):
- New "Exit tinting by causa" section with Save/Cancel/Fail buttons
that call dismissWith() through a local _dismiss-button.svelte
(same pattern as Dialog's).
Verification (playwright):
save t=83ms lastAction=saved hold=240ms (commit family)
cancel t=119ms lastAction=cancelled hold=240ms (emerge)
fail t=127ms lastAction=failed hold=600ms (alert)
Smoke 68/68, morfo-check 66/66.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* Sema alignment: causal exit reason. Updated BEFORE
|
|
|
|
|
* `data-state` flips to `'closed'` so eidos can tint the
|
|
|
|
|
* exit animation per-action. Same pattern as Dialog.
|
|
|
|
|
*/
|
|
|
|
|
attr: 'data-last-action',
|
|
|
|
|
values: ['saved', 'cancelled', 'dismissed', 'dismissed-outside', 'failed'],
|
|
|
|
|
severity: 'optional'
|
|
|
|
|
}
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
],
|
morfo: enrich 65 components with aria + keyboard + focus contracts
Each morfo now declares what its provider actually emits (via agent-based
extraction from provider code). Before: parts + data only. After: parts +
data + ARIA per part + keyboard per part + focus policy for overlays.
Contract source: provider's `readonly props = $derived.by(...)` blocks,
classified into the v.* value kinds — literal, stateRef, partRef, propRef,
translationRef. Conditions extracted from ternary / guard patterns.
Focus policies added on overlay components: popover, drawer, alert-dialog,
dropdown-menu, context-menu, select, date-picker, date-range-picker,
time-picker, time-range-picker, color-picker. Context-menu uses
return:'previous' (no discrete trigger). Tooltip, link-preview, combobox,
command omit focus (no trap).
Tag-group Link variant: morfo-check tolerates sibling parts sharing a
physical element (Link IS Item in the DOM) via allPartAttrs cross-scope.
README.md added — developer guide: authoring a morfo step-by-step, the
v.* builders, severity rules, validation layers, polymorphism handling
via defaultElement + role, Sema alignment (data-last-action, transition
markers).
Verify:
npm run check — 5 errs, all pre-existing in sium/examples.
vitest run src/uix/morfo — 7/7 pass.
morfo:check — 64/66 pass (2 flaky Playwright navigation timeouts,
re-run clean when isolated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
aria: [
|
|
|
|
|
{
|
|
|
|
|
attr: 'aria-modal',
|
|
|
|
|
value: v.literal('true'),
|
|
|
|
|
severity: 'optional',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'modal' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
attr: 'aria-labelledby',
|
|
|
|
|
value: v.partRef('title'),
|
|
|
|
|
condition: { when: 'part-present', part: 'title' },
|
|
|
|
|
severity: 'recommended'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
attr: 'aria-describedby',
|
|
|
|
|
value: v.partRef('description'),
|
|
|
|
|
condition: { when: 'part-present', part: 'description' },
|
|
|
|
|
severity: 'optional'
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
keyboard: [
|
|
|
|
|
{ key: 'Escape', action: 'close' },
|
|
|
|
|
{ key: 'Tab', action: 'focus-next' },
|
|
|
|
|
{ key: 'Shift+Tab', action: 'focus-prev' }
|
|
|
|
|
]
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Overlay',
|
|
|
|
|
kebab: 'overlay',
|
morfo: catalog `archetype` across 65 components (~265 part declarations)
Bulk classification pass that gives every part a cross-component archetype
where one applies. Done with `scripts/catalog-archetypes.ts` — a one-shot
mechanical mapping based on a kebab → archetype table, plus three manual
overrides for components where Provider IS the interactive element.
Coverage
- 440 part declarations examined.
- ~265 received an archetype (provider, trigger, content, overlay, viewport,
item, indicator, thumb, track, label, title, description, close, action,
header, image, fallback, arrow, separator, group, input, segment).
- ~175 left without one — genuinely component-specific (Slider.Range,
PinInput.Segment internals, color-picker channel-slider, etc.).
Manual overrides (Provider IS the trigger)
- Toggle, Switch, Checkbox: their `Provider` part is a `<button>` that
the user actually interacts with, not a root container. Archetype set to
`'trigger'` instead of the default `'provider'` so eidos can style them
alongside Dialog.Trigger / Popover.Trigger / etc.
Script (scripts/catalog-archetypes.ts)
- Walks every `*.ts` under `src/uix/morfo/components/`.
- Inserts `archetype: '...'` directly after the matching `kebab:` line when
a mapping exists.
- Idempotent — skips parts that already declare an archetype.
- Indent-aware — only operates on part kebabs (≥2 tabs), never the morfo
root kebab. Bug discovered + fixed mid-pass when radio-group's root
kebab `'radio-group'` matched the `'radio-group' → 'group'` alias and
inserted at the wrong level (cleaned up by hand).
- Reports per-file counts + the list of unmapped kebabs at the end so
future passes can extend the mapping.
Tests
- Updated `omits data-archetype when not declared` test to use a tiny
inline morfo (Toggle's Provider now has archetype 'trigger', so it can't
be the "no archetype" fixture anymore).
Verification
- 22/22 runtime tests, full focused suite green.
- 66/66 morfo-check (every component still validates against its DOM —
archetype is additive, doesn't change emitted attrs apart from the new
`data-archetype="..."` which morfo-check doesn't enforce yet).
- 68/68 smoke routes.
- svelte-check unchanged at 155.
6 months ago
|
|
|
archetype: 'overlay',
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'div',
|
|
|
|
|
optional: true,
|
|
|
|
|
states: ['open', 'closed'],
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
data: [
|
|
|
|
|
{ attr: 'data-state', values: ['open', 'closed'], value: v.stateRef('open') },
|
|
|
|
|
{ attr: 'data-nested', severity: 'optional' },
|
|
|
|
|
{ attr: 'data-nested-open', severity: 'optional' },
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
{
|
|
|
|
|
// Overlay mirrors the provider's variant so the recipe can
|
|
|
|
|
// hide the backdrop in `'inline'` and `'persistent'` modes
|
|
|
|
|
// directly via attribute selector — even when a consumer
|
|
|
|
|
// renders `<Drawer.Overlay>` explicitly in a non-overlay
|
|
|
|
|
// variant. The wrapper's flat form skips Overlay anyway.
|
|
|
|
|
attr: 'data-variant',
|
|
|
|
|
values: ['overlay', 'inline', 'persistent'],
|
|
|
|
|
value: v.propRef('variant')
|
|
|
|
|
}
|
|
|
|
|
],
|
morfo: enrich 65 components with aria + keyboard + focus contracts
Each morfo now declares what its provider actually emits (via agent-based
extraction from provider code). Before: parts + data only. After: parts +
data + ARIA per part + keyboard per part + focus policy for overlays.
Contract source: provider's `readonly props = $derived.by(...)` blocks,
classified into the v.* value kinds — literal, stateRef, partRef, propRef,
translationRef. Conditions extracted from ternary / guard patterns.
Focus policies added on overlay components: popover, drawer, alert-dialog,
dropdown-menu, context-menu, select, date-picker, date-range-picker,
time-picker, time-range-picker, color-picker. Context-menu uses
return:'previous' (no discrete trigger). Tooltip, link-preview, combobox,
command omit focus (no trap).
Tag-group Link variant: morfo-check tolerates sibling parts sharing a
physical element (Link IS Item in the DOM) via allPartAttrs cross-scope.
README.md added — developer guide: authoring a morfo step-by-step, the
v.* builders, severity rules, validation layers, polymorphism handling
via defaultElement + role, Sema alignment (data-last-action, transition
markers).
Verify:
npm run check — 5 errs, all pre-existing in sium/examples.
vitest run src/uix/morfo — 7/7 pass.
morfo:check — 64/66 pass (2 flaky Playwright navigation timeouts,
re-run clean when isolated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
aria: [{ attr: 'aria-hidden', value: v.literal('true') }]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Handle',
|
|
|
|
|
kebab: 'handle',
|
morfo: catalog `archetype` across 65 components (~265 part declarations)
Bulk classification pass that gives every part a cross-component archetype
where one applies. Done with `scripts/catalog-archetypes.ts` — a one-shot
mechanical mapping based on a kebab → archetype table, plus three manual
overrides for components where Provider IS the interactive element.
Coverage
- 440 part declarations examined.
- ~265 received an archetype (provider, trigger, content, overlay, viewport,
item, indicator, thumb, track, label, title, description, close, action,
header, image, fallback, arrow, separator, group, input, segment).
- ~175 left without one — genuinely component-specific (Slider.Range,
PinInput.Segment internals, color-picker channel-slider, etc.).
Manual overrides (Provider IS the trigger)
- Toggle, Switch, Checkbox: their `Provider` part is a `<button>` that
the user actually interacts with, not a root container. Archetype set to
`'trigger'` instead of the default `'provider'` so eidos can style them
alongside Dialog.Trigger / Popover.Trigger / etc.
Script (scripts/catalog-archetypes.ts)
- Walks every `*.ts` under `src/uix/morfo/components/`.
- Inserts `archetype: '...'` directly after the matching `kebab:` line when
a mapping exists.
- Idempotent — skips parts that already declare an archetype.
- Indent-aware — only operates on part kebabs (≥2 tabs), never the morfo
root kebab. Bug discovered + fixed mid-pass when radio-group's root
kebab `'radio-group'` matched the `'radio-group' → 'group'` alias and
inserted at the wrong level (cleaned up by hand).
- Reports per-file counts + the list of unmapped kebabs at the end so
future passes can extend the mapping.
Tests
- Updated `omits data-archetype when not declared` test to use a tiny
inline morfo (Toggle's Provider now has archetype 'trigger', so it can't
be the "no archetype" fixture anymore).
Verification
- 22/22 runtime tests, full focused suite green.
- 66/66 morfo-check (every component still validates against its DOM —
archetype is additive, doesn't change emitted attrs apart from the new
`data-archetype="..."` which morfo-check doesn't enforce yet).
- 68/68 smoke routes.
- svelte-check unchanged at 155.
6 months ago
|
|
|
archetype: 'thumb',
|
morfo: enrich 65 components with aria + keyboard + focus contracts
Each morfo now declares what its provider actually emits (via agent-based
extraction from provider code). Before: parts + data only. After: parts +
data + ARIA per part + keyboard per part + focus policy for overlays.
Contract source: provider's `readonly props = $derived.by(...)` blocks,
classified into the v.* value kinds — literal, stateRef, partRef, propRef,
translationRef. Conditions extracted from ternary / guard patterns.
Focus policies added on overlay components: popover, drawer, alert-dialog,
dropdown-menu, context-menu, select, date-picker, date-range-picker,
time-picker, time-range-picker, color-picker. Context-menu uses
return:'previous' (no discrete trigger). Tooltip, link-preview, combobox,
command omit focus (no trap).
Tag-group Link variant: morfo-check tolerates sibling parts sharing a
physical element (Link IS Item in the DOM) via allPartAttrs cross-scope.
README.md added — developer guide: authoring a morfo step-by-step, the
v.* builders, severity rules, validation layers, polymorphism handling
via defaultElement + role, Sema alignment (data-last-action, transition
markers).
Verify:
npm run check — 5 errs, all pre-existing in sium/examples.
vitest run src/uix/morfo — 7/7 pass.
morfo:check — 64/66 pass (2 flaky Playwright navigation timeouts,
re-run clean when isolated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'div',
|
|
|
|
|
optional: true,
|
|
|
|
|
data: [],
|
|
|
|
|
aria: [{ attr: 'aria-hidden', value: v.literal('true') }]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Title',
|
|
|
|
|
kebab: 'title',
|
morfo: catalog `archetype` across 65 components (~265 part declarations)
Bulk classification pass that gives every part a cross-component archetype
where one applies. Done with `scripts/catalog-archetypes.ts` — a one-shot
mechanical mapping based on a kebab → archetype table, plus three manual
overrides for components where Provider IS the interactive element.
Coverage
- 440 part declarations examined.
- ~265 received an archetype (provider, trigger, content, overlay, viewport,
item, indicator, thumb, track, label, title, description, close, action,
header, image, fallback, arrow, separator, group, input, segment).
- ~175 left without one — genuinely component-specific (Slider.Range,
PinInput.Segment internals, color-picker channel-slider, etc.).
Manual overrides (Provider IS the trigger)
- Toggle, Switch, Checkbox: their `Provider` part is a `<button>` that
the user actually interacts with, not a root container. Archetype set to
`'trigger'` instead of the default `'provider'` so eidos can style them
alongside Dialog.Trigger / Popover.Trigger / etc.
Script (scripts/catalog-archetypes.ts)
- Walks every `*.ts` under `src/uix/morfo/components/`.
- Inserts `archetype: '...'` directly after the matching `kebab:` line when
a mapping exists.
- Idempotent — skips parts that already declare an archetype.
- Indent-aware — only operates on part kebabs (≥2 tabs), never the morfo
root kebab. Bug discovered + fixed mid-pass when radio-group's root
kebab `'radio-group'` matched the `'radio-group' → 'group'` alias and
inserted at the wrong level (cleaned up by hand).
- Reports per-file counts + the list of unmapped kebabs at the end so
future passes can extend the mapping.
Tests
- Updated `omits data-archetype when not declared` test to use a tiny
inline morfo (Toggle's Provider now has archetype 'trigger', so it can't
be the "no archetype" fixture anymore).
Verification
- 22/22 runtime tests, full focused suite green.
- 66/66 morfo-check (every component still validates against its DOM —
archetype is additive, doesn't change emitted attrs apart from the new
`data-archetype="..."` which morfo-check doesn't enforce yet).
- 68/68 smoke routes.
- svelte-check unchanged at 155.
6 months ago
|
|
|
archetype: 'title',
|
morfo: enrich 65 components with aria + keyboard + focus contracts
Each morfo now declares what its provider actually emits (via agent-based
extraction from provider code). Before: parts + data only. After: parts +
data + ARIA per part + keyboard per part + focus policy for overlays.
Contract source: provider's `readonly props = $derived.by(...)` blocks,
classified into the v.* value kinds — literal, stateRef, partRef, propRef,
translationRef. Conditions extracted from ternary / guard patterns.
Focus policies added on overlay components: popover, drawer, alert-dialog,
dropdown-menu, context-menu, select, date-picker, date-range-picker,
time-picker, time-range-picker, color-picker. Context-menu uses
return:'previous' (no discrete trigger). Tooltip, link-preview, combobox,
command omit focus (no trap).
Tag-group Link variant: morfo-check tolerates sibling parts sharing a
physical element (Link IS Item in the DOM) via allPartAttrs cross-scope.
README.md added — developer guide: authoring a morfo step-by-step, the
v.* builders, severity rules, validation layers, polymorphism handling
via defaultElement + role, Sema alignment (data-last-action, transition
markers).
Verify:
npm run check — 5 errs, all pre-existing in sium/examples.
vitest run src/uix/morfo — 7/7 pass.
morfo:check — 64/66 pass (2 flaky Playwright navigation timeouts,
re-run clean when isolated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'div',
|
|
|
|
|
role: 'heading',
|
|
|
|
|
optional: true,
|
|
|
|
|
data: [],
|
|
|
|
|
aria: [{ attr: 'aria-level', value: v.propRef('level'), severity: 'recommended' }]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Description',
|
|
|
|
|
kebab: 'description',
|
morfo: catalog `archetype` across 65 components (~265 part declarations)
Bulk classification pass that gives every part a cross-component archetype
where one applies. Done with `scripts/catalog-archetypes.ts` — a one-shot
mechanical mapping based on a kebab → archetype table, plus three manual
overrides for components where Provider IS the interactive element.
Coverage
- 440 part declarations examined.
- ~265 received an archetype (provider, trigger, content, overlay, viewport,
item, indicator, thumb, track, label, title, description, close, action,
header, image, fallback, arrow, separator, group, input, segment).
- ~175 left without one — genuinely component-specific (Slider.Range,
PinInput.Segment internals, color-picker channel-slider, etc.).
Manual overrides (Provider IS the trigger)
- Toggle, Switch, Checkbox: their `Provider` part is a `<button>` that
the user actually interacts with, not a root container. Archetype set to
`'trigger'` instead of the default `'provider'` so eidos can style them
alongside Dialog.Trigger / Popover.Trigger / etc.
Script (scripts/catalog-archetypes.ts)
- Walks every `*.ts` under `src/uix/morfo/components/`.
- Inserts `archetype: '...'` directly after the matching `kebab:` line when
a mapping exists.
- Idempotent — skips parts that already declare an archetype.
- Indent-aware — only operates on part kebabs (≥2 tabs), never the morfo
root kebab. Bug discovered + fixed mid-pass when radio-group's root
kebab `'radio-group'` matched the `'radio-group' → 'group'` alias and
inserted at the wrong level (cleaned up by hand).
- Reports per-file counts + the list of unmapped kebabs at the end so
future passes can extend the mapping.
Tests
- Updated `omits data-archetype when not declared` test to use a tiny
inline morfo (Toggle's Provider now has archetype 'trigger', so it can't
be the "no archetype" fixture anymore).
Verification
- 22/22 runtime tests, full focused suite green.
- 66/66 morfo-check (every component still validates against its DOM —
archetype is additive, doesn't change emitted attrs apart from the new
`data-archetype="..."` which morfo-check doesn't enforce yet).
- 68/68 smoke routes.
- svelte-check unchanged at 155.
6 months ago
|
|
|
archetype: 'description',
|
morfo: enrich 65 components with aria + keyboard + focus contracts
Each morfo now declares what its provider actually emits (via agent-based
extraction from provider code). Before: parts + data only. After: parts +
data + ARIA per part + keyboard per part + focus policy for overlays.
Contract source: provider's `readonly props = $derived.by(...)` blocks,
classified into the v.* value kinds — literal, stateRef, partRef, propRef,
translationRef. Conditions extracted from ternary / guard patterns.
Focus policies added on overlay components: popover, drawer, alert-dialog,
dropdown-menu, context-menu, select, date-picker, date-range-picker,
time-picker, time-range-picker, color-picker. Context-menu uses
return:'previous' (no discrete trigger). Tooltip, link-preview, combobox,
command omit focus (no trap).
Tag-group Link variant: morfo-check tolerates sibling parts sharing a
physical element (Link IS Item in the DOM) via allPartAttrs cross-scope.
README.md added — developer guide: authoring a morfo step-by-step, the
v.* builders, severity rules, validation layers, polymorphism handling
via defaultElement + role, Sema alignment (data-last-action, transition
markers).
Verify:
npm run check — 5 errs, all pre-existing in sium/examples.
vitest run src/uix/morfo — 7/7 pass.
morfo:check — 64/66 pass (2 flaky Playwright navigation timeouts,
re-run clean when isolated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'div',
|
|
|
|
|
optional: true,
|
|
|
|
|
data: [],
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
aria: []
|
|
|
|
|
},
|
morfo: enrich 65 components with aria + keyboard + focus contracts
Each morfo now declares what its provider actually emits (via agent-based
extraction from provider code). Before: parts + data only. After: parts +
data + ARIA per part + keyboard per part + focus policy for overlays.
Contract source: provider's `readonly props = $derived.by(...)` blocks,
classified into the v.* value kinds — literal, stateRef, partRef, propRef,
translationRef. Conditions extracted from ternary / guard patterns.
Focus policies added on overlay components: popover, drawer, alert-dialog,
dropdown-menu, context-menu, select, date-picker, date-range-picker,
time-picker, time-range-picker, color-picker. Context-menu uses
return:'previous' (no discrete trigger). Tooltip, link-preview, combobox,
command omit focus (no trap).
Tag-group Link variant: morfo-check tolerates sibling parts sharing a
physical element (Link IS Item in the DOM) via allPartAttrs cross-scope.
README.md added — developer guide: authoring a morfo step-by-step, the
v.* builders, severity rules, validation layers, polymorphism handling
via defaultElement + role, Sema alignment (data-last-action, transition
markers).
Verify:
npm run check — 5 errs, all pre-existing in sium/examples.
vitest run src/uix/morfo — 7/7 pass.
morfo:check — 64/66 pass (2 flaky Playwright navigation timeouts,
re-run clean when isolated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
{
|
|
|
|
|
name: 'Close',
|
|
|
|
|
kebab: 'close',
|
morfo: catalog `archetype` across 65 components (~265 part declarations)
Bulk classification pass that gives every part a cross-component archetype
where one applies. Done with `scripts/catalog-archetypes.ts` — a one-shot
mechanical mapping based on a kebab → archetype table, plus three manual
overrides for components where Provider IS the interactive element.
Coverage
- 440 part declarations examined.
- ~265 received an archetype (provider, trigger, content, overlay, viewport,
item, indicator, thumb, track, label, title, description, close, action,
header, image, fallback, arrow, separator, group, input, segment).
- ~175 left without one — genuinely component-specific (Slider.Range,
PinInput.Segment internals, color-picker channel-slider, etc.).
Manual overrides (Provider IS the trigger)
- Toggle, Switch, Checkbox: their `Provider` part is a `<button>` that
the user actually interacts with, not a root container. Archetype set to
`'trigger'` instead of the default `'provider'` so eidos can style them
alongside Dialog.Trigger / Popover.Trigger / etc.
Script (scripts/catalog-archetypes.ts)
- Walks every `*.ts` under `src/uix/morfo/components/`.
- Inserts `archetype: '...'` directly after the matching `kebab:` line when
a mapping exists.
- Idempotent — skips parts that already declare an archetype.
- Indent-aware — only operates on part kebabs (≥2 tabs), never the morfo
root kebab. Bug discovered + fixed mid-pass when radio-group's root
kebab `'radio-group'` matched the `'radio-group' → 'group'` alias and
inserted at the wrong level (cleaned up by hand).
- Reports per-file counts + the list of unmapped kebabs at the end so
future passes can extend the mapping.
Tests
- Updated `omits data-archetype when not declared` test to use a tiny
inline morfo (Toggle's Provider now has archetype 'trigger', so it can't
be the "no archetype" fixture anymore).
Verification
- 22/22 runtime tests, full focused suite green.
- 66/66 morfo-check (every component still validates against its DOM —
archetype is additive, doesn't change emitted attrs apart from the new
`data-archetype="..."` which morfo-check doesn't enforce yet).
- 68/68 smoke routes.
- svelte-check unchanged at 155.
6 months ago
|
|
|
archetype: 'close',
|
morfo: enrich 65 components with aria + keyboard + focus contracts
Each morfo now declares what its provider actually emits (via agent-based
extraction from provider code). Before: parts + data only. After: parts +
data + ARIA per part + keyboard per part + focus policy for overlays.
Contract source: provider's `readonly props = $derived.by(...)` blocks,
classified into the v.* value kinds — literal, stateRef, partRef, propRef,
translationRef. Conditions extracted from ternary / guard patterns.
Focus policies added on overlay components: popover, drawer, alert-dialog,
dropdown-menu, context-menu, select, date-picker, date-range-picker,
time-picker, time-range-picker, color-picker. Context-menu uses
return:'previous' (no discrete trigger). Tooltip, link-preview, combobox,
command omit focus (no trap).
Tag-group Link variant: morfo-check tolerates sibling parts sharing a
physical element (Link IS Item in the DOM) via allPartAttrs cross-scope.
README.md added — developer guide: authoring a morfo step-by-step, the
v.* builders, severity rules, validation layers, polymorphism handling
via defaultElement + role, Sema alignment (data-last-action, transition
markers).
Verify:
npm run check — 5 errs, all pre-existing in sium/examples.
vitest run src/uix/morfo — 7/7 pass.
morfo:check — 64/66 pass (2 flaky Playwright navigation timeouts,
re-run clean when isolated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'button',
|
|
|
|
|
role: 'button',
|
|
|
|
|
optional: true,
|
|
|
|
|
data: [],
|
|
|
|
|
aria: [
|
|
|
|
|
{ attr: 'type', value: v.literal('button') },
|
|
|
|
|
{
|
|
|
|
|
attr: 'aria-label',
|
|
|
|
|
value: v.commonRef('buttons.close', 'Close'),
|
morfo: enrich 65 components with aria + keyboard + focus contracts
Each morfo now declares what its provider actually emits (via agent-based
extraction from provider code). Before: parts + data only. After: parts +
data + ARIA per part + keyboard per part + focus policy for overlays.
Contract source: provider's `readonly props = $derived.by(...)` blocks,
classified into the v.* value kinds — literal, stateRef, partRef, propRef,
translationRef. Conditions extracted from ternary / guard patterns.
Focus policies added on overlay components: popover, drawer, alert-dialog,
dropdown-menu, context-menu, select, date-picker, date-range-picker,
time-picker, time-range-picker, color-picker. Context-menu uses
return:'previous' (no discrete trigger). Tooltip, link-preview, combobox,
command omit focus (no trap).
Tag-group Link variant: morfo-check tolerates sibling parts sharing a
physical element (Link IS Item in the DOM) via allPartAttrs cross-scope.
README.md added — developer guide: authoring a morfo step-by-step, the
v.* builders, severity rules, validation layers, polymorphism handling
via defaultElement + role, Sema alignment (data-last-action, transition
markers).
Verify:
npm run check — 5 errs, all pre-existing in sium/examples.
vitest run src/uix/morfo — 7/7 pass.
morfo:check — 64/66 pass (2 flaky Playwright navigation timeouts,
re-run clean when isolated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
severity: 'recommended'
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
]
|
soma/morfo: typed createAttrs + 6 audit batches + form hang fix + A36
## morfo: typed createAttrs
- `types.ts`: all array fields are now `readonly` so morfos can be authored
with `as const satisfies Morfo` without casts.
- `createAttrs` is a generic `<const M extends MorfoShape>` that infers the
emitted attr map from the morfo's literal shape. Typos like `attrs.trigerr`
are now compile errors; autocomplete lists every valid part.
- All 66 morfos migrated: `export const xMorfo = { ... } as const satisfies Morfo`.
- Two latent bugs the new typing caught: `dateRangeFieldAttrs.root` and
`timeRangeFieldAttrs.root` — legacy from the root→provider refactor, fixed
to `.provider`.
- Docs (morfo/README, COMPONENT_GUIDE) mandate `as const satisfies Morfo`;
the older `: Morfo` annotation widens literals and loses autocomplete.
## Audit batches 1–7 (soma-audit-2026-04-20)
Batch 1 — Keyboard a11y (A14/A12):
- Tabs: `isFallbackTabStop(el)` using `untrack` → first trigger gets
`tabindex=0` when no tab matches (WCAG 2.1.1 was failing).
- RatingGroup: `dir` prop + `getDirectionalKeys(dir, 'horizontal')` for RTL.
- Accordion P1-1/P1-6 dismissed: roving/labelledby already correct per APG.
Batch 2 — A33 reactive collections: Select `labelRegistry` and NavigationMenu
`triggerRefs` migrated to `SvelteMap`.
Batch 3 — A31 O(N²) fix: RadioGroup lifts `hasSelection` + `isFallbackTabStop`
to the provider with `untrack`-wrapped DOM walk. Per-item derivations drop
from O(N²) to O(1) per trigger.
Batch 4 — Morfo aria sync: toast / time-field / date-field / tabs /
tag-group / navigation-menu declare every aria the provider emits
(14 entries across 6 files).
Batch 5 — Accessible naming props: Tabs.List, ToggleGroup, TreeView,
NavigationMenu expose `aria-label?` / `aria-labelledby?` with explicit
"required unless labelledby is set" JSDoc.
Batch 6 — A30 cleanup: Checkbox `setLabelId` and 5 trigger-ref providers
(select/tooltip/link-preview/dropdown-menu/context-menu) migrated from
`$effect(() => parent.ref = opts.ref)` to the `Provider` base's
`onRefChange` callback (fires via attachRef inside `untrack`).
Batch 7 — A11 wrapper thinning: stepper/tree-view wrappers removed
`ctx.get()`, providers expose proxy derivations; time/date-range-field
inputs use `untrack(() => …)` for one-shot id registration; menubar
content's 40-line keyboard nav moved to `MenubarMenuProvider.onContentKeydown`;
color-picker area + channel-slider pointer state machines moved to the
providers with `onpointerdown/move/up` emitted via `props`.
form-auto-fields deferred with justification (reflective schema renderer,
not a state-holding wrapper).
## Form hang fix (A36)
`runValidate` entered the `isPromiseLike` branch even for sync schemas
because sium's Standard-Schema adapter declares `validate: async`. The
fallback `return { errors, issues }` read both reactive vars inside the
validation `$effect` while the `.then(...)` microtask wrote them
→ microtask-mediated loop, silent to Svelte's sync effect-depth guard.
Symptom: kitchen-sink hung the tab on mount with onChange / onBlur.
Fix: `return untrack(() => ({ errors, issues }))`. Regression locked by
two new 5s-timeout tests in `form-auto-fields.svelte.test.ts`.
Pattern codified as A36 in COMPONENT_GUIDE; CLAUDE.md count updated to
A1–A36 / 39 items.
## Verification
- `npm run check`: 0 errors in soma/morfo scope (sema demo pre-existing
errors untouched).
- `npx vitest run src/uix/soma src/uix/morfo`: all tests green
(94 + 7 = 101 tests, inc. 2 new form regression tests).
- `npm run smoke`: 65/65 routes.
- `npx tsx scripts/morfo-check.ts`: 66/66 morfos.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
|
|
|
} as const satisfies Morfo;
|