Naming coherence across the part layer: the root part's `kebab` field now
matches its `name: 'Provider'`. The `createAttrs` special case still emits
`data-{component}` (no suffix) for the provider part — the DOM contract is
unchanged, only the authoring name changes.
Affected:
- createAttrs special-case: 'root' → 'provider'
- 66 morfos: kebab 'root' → 'provider'
- 77 soma provider files: `attrs.root` → `attrs.provider`, constructor
part arg 'root' → 'provider'
- Docs: A2 rule rewritten, morfo/README, soma/README example updates
- Test / schema / morfo-check: updated for new kebab
Toolbar fix: remove `itemsVersion` counter and per-item `$effect` blocks
that called `notifyItemsChanged()`. The pattern was an A30 violation —
each item's $effect read `ref.current`/`disabled` and wrote back to the
provider, which cascaded through `firstTabStop` → per-item `props`
$derived → DOM reattach → effect re-run, triggering
`effect_update_depth_exceeded` on page load. `isTabStop` now uses
`untrack` around the DOM walk so per-item `props` derivations only
depend on `lastFocusedElement`.
Verification: 7/7 morfo tests, 65/65 smoke, 66/66 morfo-check, no effect
loops detected across any demo page.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
Morfo (src/uix/morfo/): machine-readable contract of a component's public
DOM surface, shared between soma/sema/eidos. Shape v5 consolidated across
three independent AI reviews (Gemini, Grok, ChatGPT) plus Sema alignment.
Types, sium-based validator with cross-field invariants, dialog.ts as the
first canonical morfo with data-last-action for Sema-causal exits, 7 passing
unit tests.
createAttrs(morfo) and registerContract(morfo) now consume a Morfo — legacy
inline signature removed. Dialog provider refactored to derive parts +
data contract from dialogMorfo. The other 65 soma components need porting
(svelte-check lists them) — tracked work.
Tooling:
scripts/smoke-check.mjs — Playwright smoke over 65 soma demo routes
scripts/morfo-check.ts — validates emitted DOM vs morfo
scripts/morfo-vocabulary-check.ts — canonical vocabulary consistency
npm run smoke / morfo:check / morfo:vocabulary
WAI-ARIA APG components added (100% APG coverage now):
Announce, Avatar, Clipboard, DragDrop, Feed, GridList, Meter, Progress,
SearchField, TagGroup, TreeGrid + Table.RowDetail/Trigger (detail panel
pattern; hierarchical Table rows deprecated — use TreeGrid instead).
air: Link, Banner.
Sema prep:
src/uix/sema/sema_pre.md documents the semantic layer's DOM requirements.
Morfo already provides everything Sema needs (data-last-action patterns,
transition markers, enumerable state values, cross-component consistency).
Sema implementation deferred.
COMPONENT_GUIDE updated: items 37-39 (translation namespace grep, DOM
topology vs .require() audit, npm run smoke is part of done) + A34 rule
with incident log. WAI-ARIA pattern listed as soma membership criterion.
Pagination translation bug fixed (soma.pagination.page -> idlangref
PAGINATION_LANGS.PAGE). Table demo namespace fix (soma.table.*
-> components.table.*). Avatar demo now offline-safe (SVG data URL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
**Date library reorganisation** (no new third-party dependency)
- New `src/lib/util/dias/` with `_vendor/` (Adobe Apache 2.0 vendored code,
ATTRIBUTION.md preserved) and a public facade split into `types`,
`values`, `calendars`, `queries`, `operations`, `parse`, `format`,
`index`. Consumers import only from `$lib/util/dias`.
- All date-related `Intl` consolidated in `dias/format.ts` —
`DateFormatter`, cache, `resolveDateOrder`, `resolveHourCycle`,
placeholders, defaults.
- `ext/dates` now a thin reactive wrapper delegating to `dias`.
- Aligned App-layer `HourCycle` to canonical numeric form `12 | 24`
(matches `Intl.DateTimeFormat`'s `hour12`). Removed string form
`'12h' | '24h'` from `ext/app/types`, `ext/dates`,
`ext/presentation/locale-defaults`, and their tests.
- Repointed every consumer: `soma/external/dates`,
`terra/external/dates`, eight `routes/test/*` pages.
- Legacy `src/lib/util/dates/` left untouched by request.
**Tier 3 components**
- `soma/components/calendar/` — 14 parts, single + multiple selection,
`MonthSelect`/`YearSelect`, ISO/simple week numbers,
`isDateHoliday` matcher, `data-weekend` flag, navigation live-
region, RTL flip, full ARIA, 27-item checklist verified.
- `soma/components/range-calendar/` — 14 parts, range selection with
live hover preview (novel vs ark/bits/radix/react-aria), `minDays`
/ `maxDays` / `allowSingleDay`, third-click deselects only the
clicked endpoint.
- Test pages at `/test/soma/calendar` and `/test/soma/range-calendar`
with seven demos each. Links added to the soma index under a new
"Dates" section.
- READMEs with anatomy, parts, props, ARIA, keyboard, data-attrs, and
a competitive comparison table.
**Command fixes**
- Resolved O(N^2) reactive cascade: introduced
`visibleSet: $derived<Set<string>>` on the root provider;
per-item `isVisible` is now an O(1) `has()` lookup instead of
calling back into the provider, which was re-running all items'
derivations on every `itemsVersion` bump.
- Added `dir` prop with fallback to `soma.presentation.getDir()`;
horizontal arrows flip in RTL via `getDirectionalKeys`; root
receives `dir` attribute.
**Doc updates (session outcomes)**
- `CLAUDE.md`: replaced obsolete `@/glob/lib` date references with
`$lib/util/dias`; added canonical date-layer section; reinforced
`Provider` export rule and `data-{component}[-{part}]` naming
(never `data-soma-*`); clarified translation convention.
- `src/uix/soma/COMPONENT_GUIDE.md`: new checklist items 26
(component README required) and 27 (grep for data-attr
consistency); new Common Mistakes 11 (`data-soma-*` prefix), 12
(wrong date module), 13 (string `HourCycle`).
- `src/uix/soma/SOMA_ARCHITECTURE.md`: clarified data-attr naming
(canonical emitter is `createAttrs`; validator checks enum
values only, not names); new section covering date types and
formatting through `$soma/external/dates` → `$lib/util/dias`;
documented numeric `HourCycle`.
**Infrastructure**
- `PrimitiveHeaderAttributes` and `PrimitiveSelectAttributes` added
to `$soma/types`.
- `getLastFirstDayOfWeek`, `getNextLastDayOfWeek` re-exported from
`$soma/external/dates`.
- `calendar.*` and `range-calendar.*` translation blocks added to
`$soma/core/langs.ts`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Explains why soma wraps Svelte runes instead of using them directly:
compiler magic limitation, cross-class composition need, migration path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add App service compositor (langs, nums, money, dates, units, presentation)
- Add createSvelteLang() with reactivity for locale and schema changes
- Migrate 25 soma providers from getSoma()/translator.svelte to Soma.getOr()/langs.t()
- Add reactive dir resolution in all component wrappers
- Add dir HTML attribute to 12 root providers (accordion, tabs, slider, etc.)
- Fix slider RTL: range, thumb, tick use right/left based on dir
- Add table structural parts: Header, Body, Footer wrappers
- Implement ColumnPinning, ColumnSizing, Expanded in table-core
- Remove soma core formatters, translator.svelte, prop-resolvers (moved to App)
- Remove duplicate convenience getters (locale, dir, translate) from App/Soma
- Audit all 26 soma components, create audit docs with 84 issues found
- Update test page with pinning, sizing, expandable rows demos
Parts: Root, Item, Header, Trigger, Content
- Single/multiple mode via type prop
- Collapsible (single mode: click open item to close)
- Per-item and global disabled
- Keyboard: Arrow keys (orientation-aware), Home/End, loop option
- Presence on Content with data-starting/ending-style
- --soma-accordion-content-height/width CSS vars for animations
- forceMount on Content
- Header with level prop (aria-level)
- Auto-generated item values when omitted
- Demo page with 5 sections
Compared with Radix, Ark UI, Bits UI:
- Has collapsible + loop (Radix has collapsible, Bits has loop, soma has both)
- Has header level (like Bits)
- Has CSS animation vars (like all three)
- Has data-starting/ending-style (like Bits)
- Skipped ItemIndicator (Ark) — presentation concern for visual layer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Popover Content floating wrapper now includes pointer-events: auto
when preventScroll is active (same fix as Dialog).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Soma class is the framework's identity — belongs in core/, not loose in root
alongside README and architecture docs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
soma — new headless component system replacing terra:
- Provider base class with opt-in context injection
- Reactive system (state, readableActive, writableActive)
- Attrs system (createAttrs, contracts, helpers)
- Props system (mergeProps with on-prefix detection, no hardcoded event list)
- DOM utils (consolidated core, focus/, env)
- Keyboard, events, CSS, ID, config modules
- Layers: scroll, observers, visually-hidden, arrow, portal, text-selection,
dismissal (merged escape+dismissible), presence, focus-scope
- Collapsible component as first soma validation
air components:
- Pagination, Editable, PinInput (air-native), RatingGroup (air-native),
Stepper, FileUpload wrappers over terra
- Terra defaultContent refactor for children snippet pattern
- Avatar, Meter, Progress, Separator migrated to air-native
- Reactive utilities extracted to $lib/util/reactive (shared)
- $soma and $reactive aliases added
Architecture:
- soma is independent: no imports from terra or $lib/util/reactive
- Terra exports deprecated for migrated components
- README fundacional documenting all design decisions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Components:
- AlertDialog: Provider, Content (context transition), Action, Cancel + demo
- DropdownMenu: Provider (revelation sound), Content (floating), Item, Separator,
GroupHeading + checkbox/radio indicators via CSS pseudo-elements + demo
- ContextMenu: Provider, Content sharing dropdown-menu visual language + demo
Terra fix — menu variant registry:
- Add menuRegistryByVariant in menu.svelte.ts to track open menus per variant
- MenuMenuState.onOpen() closes all other menus of same variant before opening
- Prevents concurrent context-menus (or dropdown-menus) from being open simultaneously
- Registry lives in terra infrastructure, not air — clean architectural boundary
- Cleanup via onDestroyEffect when component unmounts
CSS fixes:
- DropdownMenu content: z-index: 50 for proper stacking
- Checkbox/radio items: visual indicators via ::before pseudo-elements
- Selectors duplicated for both dropdown-menu and context-menu data-attr prefixes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- getAir() is now the single entry point — returns behavior, dom, translator, logger
- behavior.play() encapsulates soundEnabled check + preset resolution
- dom.resolve() resolves ResponsiveProp using current viewport/breakpoints
- internal/ has no knowledge of config/ (correct dependency direction)
- internal/ types stripped of Air prefix; public barrel re-exports with prefix
- context.ts defines TranslatorService locally (structurally compatible with TerraTranslator)
- button/text components updated to use only getAir(), no direct terra/config imports
- AudioContext fix: withRunningContext() resumes on first user gesture before playing
- AIR_IMPLEMENTATION_GUIDE updated: 12 semantics, correct APIs, current patterns
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Command Dialog demo (⌘K style) with proper Dialog.Trigger
- Make all inline demos collapsible to prevent UI clutter
- Add keyboard shortcut (cmd+k / ctrl+k) for Command Dialog
- Add close button (✕) and onSelect handlers to close dialog
- Fix CSS import paths in air/ and terra/ test pages
- Fix various syntax errors in test pages (toggle, toggle-group, meter)
- Update Switch and RadioGroup contracts with missing attributes
- Fix missing ctx declarations in time-field and time-range-field
- Fix checkbox group import error
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add DatiTimeFormat ('12h'|'24h') and DatiDateOrder aliases to dati_types.ts;
replace numeric HourCycle throughout dati_engine so actx and tests compile
- Add missing logger field to TerraConfigState.opts
- Fix percent NumberField: use edit-mode formatter on focus/commit/watch.pre
- Fix test pages: Toolbar.Group value type (string), Progress tuple cast
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- glob/numr_types: replace NumrConfig with rich NumrFormat (style, currency,
percent, unit, notation, compactDisplay, signDisplay, grouping, dir) using
semantic type names decoupled from Intl.NumberFormatOptions
- glob/numr_engine: internal numrFormatToIntlOpts adapter keeps Intl hidden
from consumers; mergeFormats/clampDecimals for safe instance+call merging
- glob/numr_engine.svelte: updated signature to accept NumrFormat
- terra/number-field: new headless primitive (Root, Label, Input, Increment,
Decrement) with spinbutton ARIA, locale-aware format/parse via createNumr,
direction via resolveDirectionProp, name prop for hidden form input
- terra/number-field types use NumrFormat from glob, no Intl dependency
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rewrites the architecture guide to match the current directory layout:
- layers/ (was base/), config/ at top-level, dom/ as separate top-level
- utils/state/, utils/framework/, utils/collections/, utils/css/
- createTerraContext replaces new Context<T>()
- Adds Drawer as the full practical example, including the z-index note
for overlay stacking when both content and overlay share a Portal
Also adds z-index to drawer test page (.overlay: 50, .drawer: 51) so
content always renders on top of the overlay regardless of DOM insertion order.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>