@ -20,11 +20,11 @@ Document what soma includes and what it skips (with reason).
The component must meet ALL of these:
- **WAI-ARIA pattern or semantic role** — the component implements a pattern from the [ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/patterns/) (Dialog, Combobox, Treegrid, Feed, Tabs, Toolbar, …) OR a canonical ARIA role (`role="status"`, `role="meter"`, `role="progressbar"`, `role="searchbox"`, …). If the browser's native HTML gives you the right role + keyboard model with no extra behavior required (e.g. `<a>` for Link, `<hr>` for Separator, `<img>` for Image), the primitive belongs in **air**, not soma.
- **Composition of parts** — 2+ sub-components communicating via context (Provider + Trigger + Content, Provider + Row + Cell, etc.). A single-DOM wrapper is air-level styling, not headless behavior.
- **WAI-ARIA pattern or semantic role** — the component implements a pattern from the [ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/patterns/) (Dialog, Combobox, Treegrid, Feed, Tabs, Toolbar, …) OR a canonical ARIA role (`role="status"`, `role="meter"`, `role="progressbar"`, `role="searchbox"`, …). If the browser's native HTML gives you the right role + keyboard model with no extra behavior required (e.g. `<a>` for Link, `<hr>` for Separator, `<img>` for Image), the primitive belongs in **Eidos**, not Soma.
- **Composition of parts** — 2+ sub-components communicating via context (Provider + Trigger + Content, Provider + Row + Cell, etc.). A single-DOM wrapper is Eidos-level styling, not headless behavior.
- **Complex behavior** — keyboard navigation, focus management, floating, ARIA relationships, state machines, drag, form integration, or live-region coordination. Adding `role="…"` + `aria-label` to a single element is not enough.
If it fails any of these → it's air-native, not soma.
If it fails any of these → it's Eidos-native, not Soma.
Accepted exceptions (composition criterion waived when WAI-ARIA defines a tight contract):
@ -35,7 +35,7 @@ Accepted exceptions (composition criterion waived when WAI-ARIA defines a tight
```
components/{name}/
├── {name}-provider.svelte.ts ← ALL state classes (Provider subclasses)
├── {name}-provider.svelte.ts ← ALL concrete provider/state classes
├── types.ts ← ALL prop types with JSDoc + canonical field shapes
├── langs.ts ← optional idlangref constants for imperative strings
Every soma component demo at `web/routes/{name}` must expose **every public prop** of the Provider as a live control plus a Field-integration section when applicable. Not six static code snippets. Required coverage:
Every soma component demo at `web/routes/uix/components/{name}` must expose **every public prop** of the Provider as a live control plus a Field-integration section when applicable. Not six static code snippets. Required coverage:
1. Each boolean → switch/checkbox. Each enum → radio or chip group. Each number → input. Arrays (e.g. `readonlySegments`) → one toggle per valid value.
2. All format/locale/direction variants switchable (granularity, hourCycle, locale, dateOrder, dir).
@ -1028,7 +1028,7 @@ The comparison table (`## Comparison` in every component README) is a **contract
3. **Decide each `❌` / `⚠️` explicitly** — for every non-`✅`, the user approves one of:
- **Implement now** — the gap is strategic or blocks a WAI-ARIA / reference expectation. Bring it into scope and finish the component with the feature.
- **Defer to v2** — the gap exists but isn't blocking. Add it to the component's `## Out of scope (v2 roadmap)` section with: what it is, reference libraries that ship it, why deferred, cost estimate in lines. This becomes the PR backlog.
- **Drop** — the feature isn't a real gap for soma (e.g. a competitor's framework-specific quirk, or something the air layer should own). Document the reasoning and remove the row from the table.
- **Drop** — the feature isn't a real gap for Soma (e.g. a competitor's framework-specific quirk, or something Eidos should own). Document the reasoning and remove the row from the table.
4. **No silent gaps** — if a feature appears only as a footnote and nowhere else, that's a failure mode. The reader of the README should see `❌` and know it's a deliberate decision.
**Why this exists:** during the 2026-04-19 session, AlertDialog / Listbox / Carousel / NavigationMenu all shipped with strategic gaps (Escape default, range-select, multi-slide, Viewport, Sub, data-motion, skipDelayDuration) hidden inside comparison tables the user never saw in conversation. AlertDialog in particular inherited Dialog's `escapeKeydownBehavior='ignore'` default — a WAI-ARIA regression disguised as a `⚠️` row. The rule is: if the gap isn't argued explicitly, it doesn't get to ship.