docs: update SOMA_ARCHITECTURE, README, COMPONENT_GUIDE for current state
SOMA_ARCHITECTURE.md:
- §3.4: Soma.getOr() → Soma.get(), removed Soma.translate()
- §3.6: translations via langs.ts() with idlangref, not Soma.translate()
- §6: added Gesture.base/drag/resize + SafePolygon to layers table
- §7: updated service access examples to use idlangref
- §7: rewritten Texto funcional section with langs.ts pattern
- §9: added data-dragging, data-highlighted, data-resizing, --drawer-* vars
- §13: added core/langs.ts, langs.ts in component structure
- §17: checklist synced with COMPONENT_GUIDE (10 steps)
COMPONENT_GUIDE.md:
- A15: exception for pure drag handles (immediate capture)
- A16: added focus return note for non-modal
- A17: virtual focus vs DOM focus strategy
- A18: registry pattern over DOM queries
- A19: SafePolygon for hover-gap components
- A20: exit animation via Presence (dismiss flow)
- A21: contract case normalization
- A22: Dismissal isValidEvent for complex widgets
README.md:
- SafePolygon added to layers table
- Patterns section: registry, virtual focus, exit animation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
# Soma Architecture
Documento de referencia arquitectonica para `src/uix/soma` .
docs: prune obsolete audits + studies, refresh remaining references
Deleted 60 obsolete documents that no longer reflect the active-uix
state:
- 47 per-component audit logs (*-audit.md, audit_N.md, AUDIT.md)
- 5 global soma audits (AUDIT_1, codex_audit, soma-audit-04-20/21,
audit-prompt template)
- 5 closed studies (soma/study, morfo/study, morfo/DESIGN, morfo/
PROVIDER_STUDY_2026-04-23, uix/CONTINUITY_2026-04-24)
- 2 superseded refactor proposals (refactor_claude, refactor_code)
- 1 stale bug log (form/BUG-onchange-onblur-hang — repro path
src/routes/test/sium/kitchen-sink no longer exists)
Updated the remaining operational references to drop dead links and
reflect the post-cleanup architecture:
- src/uix/README.md — rewritten links section, dropped pointers to
terra/air/CONTINUITY/refactor docs, updated adom/dom paths
- src/uix/active_architecture.md — header now dated 2026-05-08, lists
the major shifts since 2026-04-26 (provider inheritance dropped,
eidos in production, doctrina parte IV, sound eager-init); pendings
section reflects the eidos migration roadmap; "see also" purged of
deleted files
- src/uix/morfo/README.md — file tree updated (removed DESIGN/study,
added compile/runtime/PERMUTATION_RUNNER); see-also pruned
- src/uix/sema/README.md — SoundChannel section now describes the
eager-init pattern (constructor-time, not lazy); pointer to
sema-implementation-guide added
- src/uix/soma/README.md — drops terra/air mentions, declares the
actual dependency direction (eidos -> soma, not the reverse)
- src/uix/soma/SOMA_ARCHITECTURE.md — §15 rewritten as "Estado actual
y deuda histórica" reflecting Provider-inheritance-drop; demo path
updated to web/routes/
- src/uix/soma/COMPONENT_GUIDE.md — A23 dates section now points to
$libs/days (no external/dates façade); demo paths swapped from
/test/soma/* to web/routes/
- src/uix/morfo/PERMUTATION_RUNNER.md — paths swapped to web/routes/
with a relocation note
- CLAUDE.md — Refactor Documents section replaced with Reference
Documents pointing to per-layer READMEs
npm run check holds at baseline (39 pre-existing errors, none new).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
`soma` es la capa de primitives headless del sistema UIX. Implementa
behavior, accesibilidad y composicion de partes; la presentacion visual
es responsabilidad de eidos. Cada componente se describe primero como
**morfo** (contrato declarativo) y soma lo materializa mediante clases
concretas de estado que registran sus partes en `SomaRuntime` .
## 1. Proposito
`soma` existe para dar una base comun sobre la que construir interfaces complejas sin repetir la misma logica de:
- contexto compartido
- control de foco
- teclado y puntero
- `aria-*`
- `data-*`
- sincronizacion de estado
- integracion con servicios transversales
- animaciones de entrada/salida
- posicionamiento flotante
Su objetivo no es ser una capa visual ni de producto. `soma` define primitives reutilizables y predecibles; la capa visual decide el look and feel.
## 2. Arquitectura de capas
```
soma → headless: behavior, accesibilidad, data-* contracts, context, servicios
eidos → visual: tokens, CSS, temas, recipes, reacciones a data-event-*
events → percepcion: sound/haptic/hold y dispatch de ocurrencias semanticas
app → producto: composicion final, contenido, logica de negocio
```
Cada capa tiene responsabilidades estrictas:
### soma aporta
- comportamiento (keyboard, focus, dismiss, scroll lock)
- accesibilidad (ARIA, roles, live regions)
- contratos `data-*` estables y validados
- contexto y composicion de partes
- servicios de runtime (`langs`, `format` , `logger` )
- sistema de animaciones (presence, data-starting/ending-style, onComplete)
- posicionamiento flotante (@floating-ui)
### La capa visual aporta
- apariencia (tokens, colores, tipografia, spacing)
- tono visual (temas light/dark, variantes)
- decisiones de diseno opinionated (sizes, recipes)
- motion CSS y reacciones visuales a `data-event-*`
- responsive design
### La capa visual NUNCA
- importa state classes internas de soma
- depende de estructura DOM incidental
- accede a propiedades privadas
- duplica behavior que soma ya resuelve
- usa `data-*` fuera de los contratos publicados
La frontera es los `data-*` attrs y las CSS variables que soma expone.
## 3. Principios de diseno
### 3.1 El desarrollador no necesita conocer los internos
Los layers, el sistema reactivo, el floating engine — son implementacion interna. El desarrollador de componentes interactua con:
- clases concretas de estado + `SomaRuntime`
- `Soma` class para servicios
- Barrel imports jerárquicos (`import { Dialog } from '$soma/components'`)
### 3.2 Un patron, no tres
Todo componente sigue el mismo patron:
1. State class concreta registra sus partes con `SomaRuntime.part(...)`
2. Wrapper `.svelte` fino convierte props → Active/State
3. Props derivados via `$derived.by` + `runtimePart.assert`
4. Contexto para comunicacion padre-hijo
Los roots sin DOM usan `ProviderOpts` (ref opcional), las partes con DOM usan
`WithRefOpts` . Las unicas excepciones son partes declarativas que pueden vivir
fuera de su provider (`AnnounceRegion`, `FeedSentinel` ): si encuentran un
provider reutilizan su `runtime` ; si no, crean un runtime propio desde el
`Soma` del scope actual.
### 3.3 Layers como behaviors, no como wrappers
Los layers se instancian en el constructor del Provider y exponen `.props` para merge. No hay nesting de componentes wrapper en template.
```ts
// Correcto: behaviors integrados
readonly focusScope = FocusScope.use({...});
readonly dismissal = Dismissal.use({...});
readonly props = $derived.by(() => this.runtimePart.assert({
...this.runtimePart.props,
...this.focusScope.props,
...this.dismissal.props,
}));
```
```svelte
<!-- Incorrecto: nesting de wrappers (patron terra) -->
< ScrollLock >
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
< FocusScope >
< DismissibleLayer >
{content}
< / DismissibleLayer >
< / FocusScope >
< / ScrollLock >
```
### 3.4 Soma es una clase, no una configuracion
`Soma` es la identidad runtime del framework. No es un archivo de configuracion — es el objeto raiz que provee servicios via context.
```ts
// En un Provider:
readonly soma = Soma.require();
const dir = this.soma.prefs.getDir();
```
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
### 3.5 Los data-\* son contrato publico
Los `data-*` attrs son la frontera entre soma y la capa visual. Cambiarlos es breaking change.
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates
**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>
6 months ago
Convencion (obligatoria, sin excepciones):
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates
**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>
6 months ago
- provider: `data-{component}` (no `data-{component}-provider` , **no `data-soma-*`** )
- parte: `data-{component}-{part}`
- estado: `data-state` , `data-disabled` , `data-side` , `data-align` , `data-orientation`
- animacion: `data-starting-style` , `data-ending-style`
- nesting: `data-nested` , `data-nested-open`
Los nombres los emite el compilador de morfo que consume `SomaRuntime` .
`createAttrs(morfo)` queda como helper tipado para `querySelector` y tooling,
no como sistema de registro ni escritura DOM. Cualquier selector CSS, cadena
en README o snippet debe coincidir exactamente con esos nombres generados. El
validador de contratos (`assertContract`) solo verifica valores enumerados, no
nombres ni presencia — la consistencia de nombres es responsabilidad del autor
del componente (checklist item 27).
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates
**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>
6 months ago
### 3.6 La accesibilidad base no se delega
soma resuelve ARIA por defecto. El consumidor no necesita añadir `role` , `aria-modal` , `aria-expanded` , `aria-controls` , etc. — el Provider los genera.
Texto funcional se resuelve via `langs.ts()` con idlangref. El catalogo propio del componente vive en `morfo.translations` y se referencia con `v.translationRef(...)` ; texto compartido como close/cancel/save vive en `common.*` y se referencia con `v.commonRef(...)` o un idlangref absoluto. `langs.ts` por componente queda como comodidad opcional para constantes imperativas, no como catalogo canonico.
### 3.7 DOM global via ActiveDom
Soma usa el `ActiveDom` del scope para escrituras gestionadas por UIX,
listeners de `document/window` , queries globales, foco imperativo y scroll de
ventana. No existe fachada `soma/events` : `dom.listen(...)` es la superficie
canonica para registrar listeners con cleanup.
Las lecturas locales de un elemento propio (`contains`, `closest` ,
`getBoundingClientRect` , `clientWidth` , `scrollTop` ) no se envuelven en
`ActiveDom` ; son parte del comportamiento local del componente.
### 3.8 Props documentadas obligatoriamente
Todas las props de todos los componentes llevan JSDoc en `types.ts` . Cada prop: descripcion, `@default` , notas de comportamiento.
### 3.9 Comparacion con referencias
Cada componente se compara con ark-ui, bits-ui y radix-ui antes de implementar. Se documentan las props que otros tienen y soma no, con justificacion.
## 3.bis Arquitectura cerrada (post-2026-04-25)
El reparto de responsabilidades entre Morfo, Soma, Sema y ADom esta cerrado en
seis piezas con responsabilidades disjuntas:
```
Morfo declara
soma/morfo/active-uix: relocate runtime + rename SomaRuntime + sema opcional
Architectural correction. Morfo was importing Svelte runes, $adom, and
$uix/sema — all violations of "morfo is pure declarative DNA". The
runtime that interprets a CompiledMorfo lives where it executes: soma.
Move:
- src/uix/morfo/runtime.svelte.ts → src/uix/soma/runtime.svelte.ts
- src/uix/morfo/runtime.svelte.test.ts → src/uix/soma/runtime.svelte.test.ts
Rename (consistent with the move; "soma's runtime", per the doctrine
"morfo declares, soma executes"):
- class MorfoRuntime → SomaRuntime
- createMorfoRuntime() → createSomaRuntime()
- type MorfoRuntimeSources → SomaRuntimeSources
- error prefixes [morfo-runtime] → [soma-runtime]
Sema is now genuinely ornamental:
- ActiveUix.semantic returns EngineSemantic | undefined (no longer
throws when no engine is configured). Components in SSR / headless
tests / audio-disabled environments stay functional.
- SomaRuntime.trigger() skips the emit step AND the target-resolution
check when no engine is present — only requires a DOM target when
something will actually be emitted.
- Soma.semantic getter returns | undefined too (was non-optional).
Consumers updated:
- src/uix/active-uix/active-uix.svelte.ts — imports createSomaRuntime
from $soma; semantic getter returns | undefined
- src/uix/active-uix/types.ts — interface SomaRuntime, semantic | undefined
- 8 providers (toggle, switch, collapsible, dialog, drawer, popover,
toast, avatar) — type SomaRuntime, import path $soma
- src/uix/soma/index.ts — re-exports createSomaRuntime + types
- src/uix/morfo/index.ts — drops the runtime re-exports (morfo is pure)
- src/uix/sema/signal.ts — JSDoc reference updated
- src/uix/soma/core/soma.svelte.ts — semantic getter | undefined
Test added: SomaRuntime.trigger() succeeds without a semantic engine
(skips emit + target check, runs handler directly).
Docs aligned to the new shape:
- active_architecture.md — runtime locations, sema as ornamental
- soma/SOMA_ARCHITECTURE.md, soma/README.md — name + import paths
- morfo/README.md — file listing now flags morfo as PURE TS, points
at soma/runtime.svelte.ts for the interpreter
- sema/README.md — SomaRuntime references
- README.md — root architecture diagram
- CLAUDE.md — hand-off note
Tests 218/218 green. npm run check holds at the 39-error baseline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
SomaRuntime transcribe (vive en soma/)
Provider aporta sources, targets y handlers
Effects sincronizan attrs derivados
EngineSemantic despacha senales a canales perceptivos
VisualChannel materializa la senal en el DOM (data-event*, hold, cleanup)
ADom aplica mutaciones DOM (commit estructural)
```
soma/morfo/active-uix: relocate runtime + rename SomaRuntime + sema opcional
Architectural correction. Morfo was importing Svelte runes, $adom, and
$uix/sema — all violations of "morfo is pure declarative DNA". The
runtime that interprets a CompiledMorfo lives where it executes: soma.
Move:
- src/uix/morfo/runtime.svelte.ts → src/uix/soma/runtime.svelte.ts
- src/uix/morfo/runtime.svelte.test.ts → src/uix/soma/runtime.svelte.test.ts
Rename (consistent with the move; "soma's runtime", per the doctrine
"morfo declares, soma executes"):
- class MorfoRuntime → SomaRuntime
- createMorfoRuntime() → createSomaRuntime()
- type MorfoRuntimeSources → SomaRuntimeSources
- error prefixes [morfo-runtime] → [soma-runtime]
Sema is now genuinely ornamental:
- ActiveUix.semantic returns EngineSemantic | undefined (no longer
throws when no engine is configured). Components in SSR / headless
tests / audio-disabled environments stay functional.
- SomaRuntime.trigger() skips the emit step AND the target-resolution
check when no engine is present — only requires a DOM target when
something will actually be emitted.
- Soma.semantic getter returns | undefined too (was non-optional).
Consumers updated:
- src/uix/active-uix/active-uix.svelte.ts — imports createSomaRuntime
from $soma; semantic getter returns | undefined
- src/uix/active-uix/types.ts — interface SomaRuntime, semantic | undefined
- 8 providers (toggle, switch, collapsible, dialog, drawer, popover,
toast, avatar) — type SomaRuntime, import path $soma
- src/uix/soma/index.ts — re-exports createSomaRuntime + types
- src/uix/morfo/index.ts — drops the runtime re-exports (morfo is pure)
- src/uix/sema/signal.ts — JSDoc reference updated
- src/uix/soma/core/soma.svelte.ts — semantic getter | undefined
Test added: SomaRuntime.trigger() succeeds without a semantic engine
(skips emit + target check, runs handler directly).
Docs aligned to the new shape:
- active_architecture.md — runtime locations, sema as ornamental
- soma/SOMA_ARCHITECTURE.md, soma/README.md — name + import paths
- morfo/README.md — file listing now flags morfo as PURE TS, points
at soma/runtime.svelte.ts for the interpreter
- sema/README.md — SomaRuntime references
- README.md — root architecture diagram
- CLAUDE.md — hand-off note
Tests 218/218 green. npm run check holds at the 39-error baseline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
### SomaRuntime — la pieza nueva
soma/morfo/active-uix: relocate runtime + rename SomaRuntime + sema opcional
Architectural correction. Morfo was importing Svelte runes, $adom, and
$uix/sema — all violations of "morfo is pure declarative DNA". The
runtime that interprets a CompiledMorfo lives where it executes: soma.
Move:
- src/uix/morfo/runtime.svelte.ts → src/uix/soma/runtime.svelte.ts
- src/uix/morfo/runtime.svelte.test.ts → src/uix/soma/runtime.svelte.test.ts
Rename (consistent with the move; "soma's runtime", per the doctrine
"morfo declares, soma executes"):
- class MorfoRuntime → SomaRuntime
- createMorfoRuntime() → createSomaRuntime()
- type MorfoRuntimeSources → SomaRuntimeSources
- error prefixes [morfo-runtime] → [soma-runtime]
Sema is now genuinely ornamental:
- ActiveUix.semantic returns EngineSemantic | undefined (no longer
throws when no engine is configured). Components in SSR / headless
tests / audio-disabled environments stay functional.
- SomaRuntime.trigger() skips the emit step AND the target-resolution
check when no engine is present — only requires a DOM target when
something will actually be emitted.
- Soma.semantic getter returns | undefined too (was non-optional).
Consumers updated:
- src/uix/active-uix/active-uix.svelte.ts — imports createSomaRuntime
from $soma; semantic getter returns | undefined
- src/uix/active-uix/types.ts — interface SomaRuntime, semantic | undefined
- 8 providers (toggle, switch, collapsible, dialog, drawer, popover,
toast, avatar) — type SomaRuntime, import path $soma
- src/uix/soma/index.ts — re-exports createSomaRuntime + types
- src/uix/morfo/index.ts — drops the runtime re-exports (morfo is pure)
- src/uix/sema/signal.ts — JSDoc reference updated
- src/uix/soma/core/soma.svelte.ts — semantic getter | undefined
Test added: SomaRuntime.trigger() succeeds without a semantic engine
(skips emit + target check, runs handler directly).
Docs aligned to the new shape:
- active_architecture.md — runtime locations, sema as ornamental
- soma/SOMA_ARCHITECTURE.md, soma/README.md — name + import paths
- morfo/README.md — file listing now flags morfo as PURE TS, points
at soma/runtime.svelte.ts for the interpreter
- sema/README.md — SomaRuntime references
- README.md — root architecture diagram
- CLAUDE.md — hand-off note
Tests 218/218 green. npm run check holds at the 39-error baseline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
`SomaRuntime` es la pieza que faltaba entre `Morfo` (declaracion) y
`Provider` (ejecucion). Lee el morfo y produce el comportamiento.
Una instancia por componente:
```ts
readonly soma = Soma.require();
readonly runtime = this.soma.runtime(morfo, {
states: { open: () => this.opts.open.current },
props: { disabled: () => this.opts.disabled.current },
parts: { content: () => this.contentId.current },
events: {
open: () => {
this.opts.open.current = true;
},
'close-cancel': () => {
this.opts.open.current = false;
}
}
});
```
API V1:
- `runtime.part(part, opts)` — unica API publica para registrar una parte.
Devuelve el handle (`props`, `resolveProps` , `assert` ) y, con
`syncAttrs: true` , sincroniza attrs derivados por morfo via `dom.apply` .
docs: cross-layer articulation — eidos README + 2-of-3 rule across all layer docs
Closes the documentation loop on the cross-layer extension pass: morfo
now articulates between soma, sema, and (future) eidos. The "2-of-3 rule"
formalizes when an extension to morfo is justified vs when it should
stay as provider logic.
src/uix/eidos/README.md (new)
- Documents eidos's role and what it consumes from morfo + sema BEFORE
any code exists, so the contract is preparedly clean when implementation
starts.
- Catalogs which morfo fields eidos reads (parts, archetype, states,
data values, events, prewrite, focus, supportsNesting) and which it
ignores (computed state, runtime internals, layers).
- Documents the DOM-as-channel pattern: sema writes data-event* on emit;
eidos reacts to selectors like `[data-event^="dismiss"]`.
- Establishes the boundary with `air` (dead branch reference, not base).
src/uix/README.md (top-level)
- §8 Reglas de dependencia: adds the 2-of-3 rule table making the
morfo-extension contract explicit, plus a list of canonical vocabularies
(archetypes, verbs).
- §10 Reading order: includes eidos README + lib/dom + clarifies which
layers are dead branches.
src/uix/morfo/README.md
- New "Archetypes" section documenting the 24-verb vocabulary, the
Provider-as-trigger vs Provider-as-container distinction, and the rule
for adding new archetypes (≥2 components share the role).
- New "The 2-of-3 rule" section with the same table as the top-level,
listing which extensions did/didn't make it past the rule and why.
- `parts[].archetype` mentioned in the "What morfo contains" list.
src/uix/sema/README.md
- New "Vocabulario canónico de verbs" section listing SEMA_VERBS by
family and the `{verb}-{variant}` composite naming convention.
- Documents `validateEventName()` as advisory tooling.
src/uix/soma/SOMA_ARCHITECTURE.md
- partProps documentation now mentions data-archetype emission.
- New "Cross-layer hooks que soma emite por la regla 2-de-3" section
listing the data-* attrs soma writes that sema and eidos consume.
- Reading-order links updated.
No code changes — all docs.
6 months ago
- `runtime.partProps(part)` — devuelve `{ id, ref, marker, data-archetype? }` . Solo identidad estatica (el `data-archetype` es classification cross-component, nunca cambia).
- `runtime.keydown(part, event)` — dispatch de teclas declaradas en `morfo.keyboard` .
- `runtime.trigger(eventName)` — orquesta la secuencia perceptiva + state.
### Provider en el modelo nuevo
El provider deja de tener helpers locales de resolucion de morfo. Solo
aporta:
- getters reactivos para `states` , `props` , `parts`
- handlers sincronos para los `events`
- glue de layers ortogonales (Presence, Dismissal, ScrollLock)
Cada part-provider conserva el handle devuelto por `runtime.part(...)` :
```ts
readonly runtimePart = runtime.part('trigger', {
id: opts.id,
ref: opts.ref,
owner: this,
syncAttrs: true
});
readonly props = $derived.by(() => this.runtimePart.props);
```
### Tres operaciones que cubren todos los escenarios
```ts
// Cambio estructural sin senal
provider.commitState(change);
// Cambio estructural con senal
provider.commitState(change, event);
// Senal sin cambio estructural
provider.emitEvent(event);
```
Internamente:
```ts
async commitState(change, event?) {
if (event) await this.soma.events?.emit(event);
this.soma.dom.apply(change);
}
emitEvent(event) {
void this.soma.events?.emit(event);
}
```
### La secuencia de `runtime.trigger(eventName)`
```
1. prewrite imperativo (transient markers como data-last-action)
2. await events.emit(event)
3. handler sincrono del provider muta state
4. effects derivan y aplican attrs estructurales (data-state, aria-*)
```
Los effects del runtime escuchan los sources reactivos y reaplican attrs cada
vez que el estado cambia. ADom es el unico escritor de attrs mutables.
### Reglas operativas
- `partProps(part)` solo emite identidad estatica. Lo mutable lo escribe ADom.
- Lo que `dom.apply` escribe, Svelte no lo renderiza desde `partProps` .
- Event handlers son sincronos. Async va antes del trigger.
- Guards (`if (disabled) return`) van en el call-site, no dentro del handler.
- `events` /`VisualChannel` pueden usar `ActiveDom` a traves del projector
inyectado; `ActiveDom` no conoce `events` .
- `morfo.events.commits` es descriptivo, no ejecutable. El smoke valida.
### Pilotaje (orden incremental)
1. **Toggle** — primer caso, solo `partProps` .
2. **Collapsible** — anade `keydown` .
3. **Toast** — primer test real de `trigger()` con `intent` .
4. **Dialog** — al final, cuando layers + portal ya esten validados.
Ver tambien:
docs: cross-layer articulation — eidos README + 2-of-3 rule across all layer docs
Closes the documentation loop on the cross-layer extension pass: morfo
now articulates between soma, sema, and (future) eidos. The "2-of-3 rule"
formalizes when an extension to morfo is justified vs when it should
stay as provider logic.
src/uix/eidos/README.md (new)
- Documents eidos's role and what it consumes from morfo + sema BEFORE
any code exists, so the contract is preparedly clean when implementation
starts.
- Catalogs which morfo fields eidos reads (parts, archetype, states,
data values, events, prewrite, focus, supportsNesting) and which it
ignores (computed state, runtime internals, layers).
- Documents the DOM-as-channel pattern: sema writes data-event* on emit;
eidos reacts to selectors like `[data-event^="dismiss"]`.
- Establishes the boundary with `air` (dead branch reference, not base).
src/uix/README.md (top-level)
- §8 Reglas de dependencia: adds the 2-of-3 rule table making the
morfo-extension contract explicit, plus a list of canonical vocabularies
(archetypes, verbs).
- §10 Reading order: includes eidos README + lib/dom + clarifies which
layers are dead branches.
src/uix/morfo/README.md
- New "Archetypes" section documenting the 24-verb vocabulary, the
Provider-as-trigger vs Provider-as-container distinction, and the rule
for adding new archetypes (≥2 components share the role).
- New "The 2-of-3 rule" section with the same table as the top-level,
listing which extensions did/didn't make it past the rule and why.
- `parts[].archetype` mentioned in the "What morfo contains" list.
src/uix/sema/README.md
- New "Vocabulario canónico de verbs" section listing SEMA_VERBS by
family and the `{verb}-{variant}` composite naming convention.
- Documents `validateEventName()` as advisory tooling.
src/uix/soma/SOMA_ARCHITECTURE.md
- partProps documentation now mentions data-archetype emission.
- New "Cross-layer hooks que soma emite por la regla 2-de-3" section
listing the data-* attrs soma writes that sema and eidos consume.
- Reading-order links updated.
No code changes — all docs.
6 months ago
- [src/uix/morfo/README.md ](../morfo/README.md ) — declaracion, archetypes, regla 2-de-3
- [src/uix/sema/README.md ](../sema/README.md ) — contrato de `emit` , vocabulario de verbs
- [src/uix/adom/README.md ](../adom/README.md ) — `dom.apply`
docs: cross-layer articulation — eidos README + 2-of-3 rule across all layer docs
Closes the documentation loop on the cross-layer extension pass: morfo
now articulates between soma, sema, and (future) eidos. The "2-of-3 rule"
formalizes when an extension to morfo is justified vs when it should
stay as provider logic.
src/uix/eidos/README.md (new)
- Documents eidos's role and what it consumes from morfo + sema BEFORE
any code exists, so the contract is preparedly clean when implementation
starts.
- Catalogs which morfo fields eidos reads (parts, archetype, states,
data values, events, prewrite, focus, supportsNesting) and which it
ignores (computed state, runtime internals, layers).
- Documents the DOM-as-channel pattern: sema writes data-event* on emit;
eidos reacts to selectors like `[data-event^="dismiss"]`.
- Establishes the boundary with `air` (dead branch reference, not base).
src/uix/README.md (top-level)
- §8 Reglas de dependencia: adds the 2-of-3 rule table making the
morfo-extension contract explicit, plus a list of canonical vocabularies
(archetypes, verbs).
- §10 Reading order: includes eidos README + lib/dom + clarifies which
layers are dead branches.
src/uix/morfo/README.md
- New "Archetypes" section documenting the 24-verb vocabulary, the
Provider-as-trigger vs Provider-as-container distinction, and the rule
for adding new archetypes (≥2 components share the role).
- New "The 2-of-3 rule" section with the same table as the top-level,
listing which extensions did/didn't make it past the rule and why.
- `parts[].archetype` mentioned in the "What morfo contains" list.
src/uix/sema/README.md
- New "Vocabulario canónico de verbs" section listing SEMA_VERBS by
family and the `{verb}-{variant}` composite naming convention.
- Documents `validateEventName()` as advisory tooling.
src/uix/soma/SOMA_ARCHITECTURE.md
- partProps documentation now mentions data-archetype emission.
- New "Cross-layer hooks que soma emite por la regla 2-de-3" section
listing the data-* attrs soma writes that sema and eidos consume.
- Reading-order links updated.
No code changes — all docs.
6 months ago
- [src/uix/eidos/README.md ](../eidos/README.md ) — qué consume eidos del DOM
- [src/uix/README.md ](../README.md ) §2.bis — vista cross-layer
### Cross-layer hooks que soma emite por la regla 2-de-3
Soma escribe al DOM no solo lo que necesita; escribe también lo que sema y
eidos van a consumir. La regla "2-de-3" justifica qué entra al morfo y por
tanto qué emite el runtime:
- **`data-archetype`** — emitido por `partProps` cuando la parte declara
archetype. Eidos lo usa para selectores transversales
(`[data-archetype=trigger] { ... }`); sema puede asociar verbs por
archetype.
- **`data-event*`** — emitido por `events.emit` (a través del VisualChannel)
durante un hold configurable (240ms emerge/commit/handle, 600ms alert/sustain
por defecto). Eidos lo usa para tintar transiciones de eventos
(`[data-event^=dismiss]`).
docs: cross-layer articulation — eidos README + 2-of-3 rule across all layer docs
Closes the documentation loop on the cross-layer extension pass: morfo
now articulates between soma, sema, and (future) eidos. The "2-of-3 rule"
formalizes when an extension to morfo is justified vs when it should
stay as provider logic.
src/uix/eidos/README.md (new)
- Documents eidos's role and what it consumes from morfo + sema BEFORE
any code exists, so the contract is preparedly clean when implementation
starts.
- Catalogs which morfo fields eidos reads (parts, archetype, states,
data values, events, prewrite, focus, supportsNesting) and which it
ignores (computed state, runtime internals, layers).
- Documents the DOM-as-channel pattern: sema writes data-event* on emit;
eidos reacts to selectors like `[data-event^="dismiss"]`.
- Establishes the boundary with `air` (dead branch reference, not base).
src/uix/README.md (top-level)
- §8 Reglas de dependencia: adds the 2-of-3 rule table making the
morfo-extension contract explicit, plus a list of canonical vocabularies
(archetypes, verbs).
- §10 Reading order: includes eidos README + lib/dom + clarifies which
layers are dead branches.
src/uix/morfo/README.md
- New "Archetypes" section documenting the 24-verb vocabulary, the
Provider-as-trigger vs Provider-as-container distinction, and the rule
for adding new archetypes (≥2 components share the role).
- New "The 2-of-3 rule" section with the same table as the top-level,
listing which extensions did/didn't make it past the rule and why.
- `parts[].archetype` mentioned in the "What morfo contains" list.
src/uix/sema/README.md
- New "Vocabulario canónico de verbs" section listing SEMA_VERBS by
family and the `{verb}-{variant}` composite naming convention.
- Documents `validateEventName()` as advisory tooling.
src/uix/soma/SOMA_ARCHITECTURE.md
- partProps documentation now mentions data-archetype emission.
- New "Cross-layer hooks que soma emite por la regla 2-de-3" section
listing the data-* attrs soma writes that sema and eidos consume.
- Reading-order links updated.
No code changes — all docs.
6 months ago
- **`data-{component}` / `data-{component}-{part}` ** — los markers
estructurales clásicos. Eidos los usa para selectores per-componente.
## 4. Modelo de componente
La forma base de soma es `Componente.Parte` :
```ts
import { Dialog } from '$soma/components';
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
Dialog.Provider; // root — crea contexto
Dialog.Trigger; // accion — abre/cierra
Dialog.Content; // contenido — layers integrados
Dialog.Overlay; // fondo — presence
Dialog.Title; // metadata ARIA
Dialog.Description; // metadata ARIA
Dialog.Close; // accion — cierra
```
### Provider (root)
Crea el estado central, lo registra en context, gestiona presence para content y overlay. Puede o no renderizar DOM:
- **Con DOM** (Collapsible, Accordion): usa `WithRefOpts` , renderiza `<div>`
- **Sin DOM** (Dialog, Popover): usa `ProviderOpts` , solo renderiza children
### Subcomponentes
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
Leen estado del root via `.require()` . No reimplementan logica — derivan props, ARIA, data-\*, events del estado del padre.
### Portal
Componente interno (`components/internal/portal.svelte`). Renderiza hijos en otro nodo DOM. El contexto de Svelte se preserva.
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
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>
6 months ago
### Picker composition (shared state across providers)
Pickers (`DatePicker`, `DateRangePicker` , `TimePicker` , y futuros `TimeRangePicker` ) no reimplementan Popover / Field / Calendar — los **componen** con estado compartido. El root wrapper crea tres (o más) Providers que apuntan a los mismos `writableActive` refs:
```ts
// Root wrapper
const sharedValue = writableActive(() => value, (v) => (value = v));
const sharedPlaceholder = writableActive(() => placeholder, (v) => (placeholder = v));
const sharedOpen = writableActive(() => open, (v) => (open = v));
{Name}PickerProvider.create({ value: sharedValue, placeholder: sharedPlaceholder, open: sharedOpen, ...config });
PopoverProvider.create({ open: sharedOpen, ... });
{Base}FieldProvider.create({ value: sharedValue, placeholder: sharedPlaceholder, ...config });
// Calendar/RangeCalendar/slider providers are created in their own wrapper (DatePicker.Calendar, TimePicker.HourSlider, …)
```
El picker expone **solo** wrappers únicos para `Provider` , `Trigger` , y el puente al calendario/slider. Los demás exports re-exportan desde los componentes compuestos — sus `data-*` nativos (`data-popover-*`, `data-date-field-*` , `data-calendar-*` , `data-slider-*` ) siguen siendo la API de estilo autoritativa. El picker sólo añade atributos de identidad (`data-{picker}-trigger`, `data-{picker}-calendar` ) en sus propios wrappers.
**Auto-close / auto-anchor**: el PickerProvider expone `handleSelect()` que el wrapper del calendario llama al completarse una selección. Los range pickers re-anclan `placeholder` para que el mes final caiga en la columna más a la derecha visible, evitando mostrar al usuario un mes que no contiene su selección.
Ver A27 en `COMPONENT_GUIDE.md` para el checklist completo.
## 5. Runtime parts
```ts
readonly soma = Soma.require();
readonly runtime = this.soma.runtime(accordionMorfo, {});
const runtimePart = this.runtime.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: AccordionProvider.ctx
});
readonly props = $derived.by(() =>
runtimePart.assert({
...runtimePart.props,
'data-state': this.state
})
);
```
`SomaRuntime.part()` centraliza lo mecanico de cada parte:
```ts
interface SomaRuntimePart {
readonly attachment: RefAttachment | undefined;
readonly props: Record< string , unknown > ;
resolveProps(bindings?): Record< string , unknown > ;
assert< P extends Record < string , unknown > >(props: P): P;
}
```
`syncAttrs: true` activa la escritura imperativa por `uix.dom` para partes
cuyos sources ya estan declarados en el runtime. Si un provider sigue
componiendo attrs en render props, no activa `syncAttrs` .
Dos interfaces de opts:
- `ProviderOpts` — `{ id: Active<string>; ref?: State<HTMLElement | null> }` — para roots sin DOM
- `WithRefOpts` — `{ id: Active<string>; ref: State<HTMLElement | null> }` — para parts con DOM
## 6. Layers
`layers/` contiene solo clases de comportamiento (`.svelte.ts`). Son infraestructura consumida por Providers, nunca directamente por el consumidor.
### Inventario
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
| Layer | API | Responsabilidad |
| ----------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `Presence` | `new Presence(opts)` | Mount/unmount con animaciones. `isPresent` , `transitionAttrs` , `onComplete` . |
| `FocusScope` | `FocusScope.use(opts)` | Focus trap, loop, auto-focus, restore. Singleton manager con stack. |
| `Dismissal` | `Dismissal.use(opts)` | Escape + click-outside. Registry global. Behaviors: close, ignore, defer. |
| `TextSelection` | `TextSelection.use(opts)` | Previene selection overflow durante drag. |
| `ScrollLock` | `new ScrollLock(initial?, delay?)` | Body scroll lock con refcount. Soporta delay para animaciones. |
| `ResizeObserver$` | `new ResizeObserver$(getter, cb)` | ResizeObserver con lifecycle Svelte. |
| `Floating*` | `FloatingProvider.create()` , `FloatingContent.create(opts)` , etc. | Posicionamiento relativo a anchor via @floating -ui. |
docs: update SOMA_ARCHITECTURE, README, COMPONENT_GUIDE for current state
SOMA_ARCHITECTURE.md:
- §3.4: Soma.getOr() → Soma.get(), removed Soma.translate()
- §3.6: translations via langs.ts() with idlangref, not Soma.translate()
- §6: added Gesture.base/drag/resize + SafePolygon to layers table
- §7: updated service access examples to use idlangref
- §7: rewritten Texto funcional section with langs.ts pattern
- §9: added data-dragging, data-highlighted, data-resizing, --drawer-* vars
- §13: added core/langs.ts, langs.ts in component structure
- §17: checklist synced with COMPONENT_GUIDE (10 steps)
COMPONENT_GUIDE.md:
- A15: exception for pure drag handles (immediate capture)
- A16: added focus return note for non-modal
- A17: virtual focus vs DOM focus strategy
- A18: registry pattern over DOM queries
- A19: SafePolygon for hover-gap components
- A20: exit animation via Presence (dismiss flow)
- A21: contract case normalization
- A22: Dismissal isValidEvent for complex widgets
README.md:
- SafePolygon added to layers table
- Patterns section: registry, virtual focus, exit animation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
| `Gesture.base` | `Gesture.base(opts)` | Pointer tracking + axis lock + velocity. |
| `Gesture.drag` | `Gesture.drag(opts)` | Base + progress + snap points + dismiss. |
| `Gesture.resize` | `Gesture.resize(opts)` | Base + delta + min/max constraints. |
| `SafePolygon` | `new SafePolygon(opts)` | Hover-gap corridor between trigger↔content. |
`layers/floating/placement.ts` es la fuente unica para `Side` , `Align` ,
`Boundary` , `SIDE_OPTIONS` y `ALIGN_OPTIONS` . `floating/types.ts` consume esa
fuente y no importa del runtime `floating.svelte.ts` , evitando ciclos entre
tipos y clases.
### Cobertura P0 actual
Los tests por componente estan creciendo desde las piezas de mayor riesgo. A
2026-05-15 existen tests directos para:
- `$libs/datagrid/table-core.svelte.test.ts` — motor de data grid.
- `$libs/forms/form-core.svelte.test.ts` — motor de formularios + Standard Schema.
- `dialog/dialog-provider.svelte.test.ts` — open/close, fallback target y
eventos morfo.
- `drawer/drawer-provider.svelte.test.ts` — direccion logica, dismiss y modo
persistent.
- `command/command-provider.svelte.test.ts` — filtro, visibilidad, navegación
y selección.
- `combobox/combobox-provider.svelte.test.ts` — selección, `inputValue` y
navegación sin items disabled.
- `select/select-provider.svelte.test.ts` — selección single/multiple y
navegación sin items disabled.
- `popover/popover-provider.svelte.test.ts` — toggle, hover timers y señales
runtime.
- `toast/toaster.svelte.test.ts` — overflow, dismiss/remove y promesas.
- `calendar/calendar-provider.svelte.test.ts` — placeholder, meses, selección
y flags.
- `range-calendar/range-calendar-provider.svelte.test.ts` — placeholder,
orden de endpoints y limites min/max.
- `date-field/date-field-provider.svelte.test.ts` — helpers UI de segmentos,
lectura DOM, navegación por `ActiveDom` , validación y commit segmentado.
- `date-picker/date-picker-provider.svelte.test.ts` — registro runtime y
cierre controlado por `closeOnDateSelect` .
- `date-range-picker/date-range-picker-provider.svelte.test.ts` — registro
runtime y cierre controlado por `closeOnRangeSelect` .
- `time-picker/time-picker-provider.svelte.test.ts` — registro runtime,
valores de placeholder y escritura de sliders al valor compartido.
- `time-range-picker/time-range-picker-provider.svelte.test.ts` — valores por
endpoint, escritura de sliders y cierre al completar rango.
- `time-range-field/time-range-field-provider.svelte.test.ts` — validación de
orden/min/max, validación custom y foco de label via `ActiveDom` .
- `time-field/time-field-provider.svelte.test.ts` — validación, sincronización
de segmentos 12h y commit solo cuando los segmentos renderizados estan
completos.
- `date-range-field/date-range-field-provider.svelte.test.ts` — validación de
orden/min/max, validación custom y foco de label via `ActiveDom` .
- `virtual-list/virtual-list-provider.svelte.test.ts` — cálculo de ventana,
`scrollToIndex` y compensación anti-jump via `ActiveDom` .
- `virtual-grid/virtual-grid-provider.svelte.test.ts` — cálculo de ventana 2D
y `scrollToCell` via `ActiveDom` .
- `number-field/number-field-provider.svelte.test.ts` — parsing localizable,
teclado spinbutton, triggers, props ARIA y scrubber.
- `file-upload/file-upload-provider.svelte.test.ts` — aceptación/rechazo,
dropzone, input oculto, items, progress y acciones remove/clear.
- `color-field/color-field-provider.svelte.test.ts` — commit por segmentos,
edición hex por teclado, selector de formato, input oculto y foco via
`ActiveDom` .
- `color-picker/color-picker-provider.svelte.test.ts` — helpers de canales,
trigger/value/hidden, area 2D, slider de canal y swatches.
- `navigation-menu/navigation-menu-provider.svelte.test.ts` — timers UIX,
enlace item/trigger/content, foco por teclado y props de list/link.
- `tree-grid/tree-grid-provider.svelte.test.ts` — expansión, selección/rango,
filas visibles, navegación y props de row/cell/header/expand trigger.
- `dropdown-menu/dropdown-menu-provider.svelte.test.ts` — trigger open/close,
scoping de items, selección, checkbox/radio groups, submenu y group/separator.
- `context-menu/context-menu-provider.svelte.test.ts` — apertura por
`contextmenu` , anchor virtual, scoping de items, selección, checkbox/radio,
submenu y group/separator.
- `menubar/menubar-provider.svelte.test.ts` — coordinación de menús hermanos,
hover-follow, navegación horizontal y cambio de menú desde content.
- `listbox/listbox-provider.svelte.test.ts` — props ARIA root, selección,
navegación, typeahead, indicador de item y grupos.
- `tree-view/tree-view-provider.svelte.test.ts` — expansión/selección,
navegación root, typeahead, props de ramas/hojas y partes auxiliares.
- `accordion/accordion-provider.svelte.test.ts` — modos single/multiple,
eventos `open/close` por item, navegación de triggers y header/content.
- `checkbox/checkbox-provider.svelte.test.ts` — commits check/uncheck,
indeterminate, hidden input y sincronización con `CheckboxGroup` .
- `radio-group/radio-group-provider.svelte.test.ts` — roving tabindex,
selección síncrona, navegación con auto-select, hidden input y guardas.
- `switch/switch-provider.svelte.test.ts` — `commit-toggle` runtime,
keyboard, hidden input, integración con `FieldProvider` y thumb.
- `toggle/toggle-provider.svelte.test.ts` — `commit-toggle` runtime, attrs DOM
via `ActiveDom` , integración con `FieldProvider` y guardas.
- `toggle-group/toggle-group-provider.svelte.test.ts` — modos single/multiple,
roving tabindex y navegación de items saltando disabled.
- `tabs/tabs-provider.svelte.test.ts` — registros trigger/content, activacion
automatic/manual, navegacion roving, fallback tab stop y partes auxiliares.
- `slider/slider-provider.svelte.test.ts` — contrato provider/range/thumb/tick,
snapping/clamping, drag pointer confirmado y teclado de thumb.
- `progress/progress-provider.svelte.test.ts` y
`meter/meter-provider.svelte.test.ts` — `data-value/data-min/data-max`
emitidos por morfo/runtime, estados derivados e indicador sincronizado.
- `collapsible/collapsible-provider.svelte.test.ts` — refs cruzadas
trigger/content, eventos `expand/collapse` y guardas disabled.
Pendiente: seguir ampliando cobertura por el resto del catalogo Soma, ya por
componentes de riesgo medio y familias menos centrales. `table-core` ,
`form-core` y el scorer de Command ya no viven dentro de Soma: se consumen
desde `$libs/datagrid` , `$libs/forms` y `$libs/strings` .
### Convencion
- `.use(opts)` → lifecycle auto-gestionado (watch/$effect internos). Constructor privado.
- `new X(opts)` → lifecycle manual. El consumidor controla.
- `.props` → objeto para spread en el Provider.
### Animaciones (Presence)
Lifecycle:
```
OPENING:
open=true → shouldRender=true + data-starting-style
→ next rAF: data-starting-style removed (triggers CSS transition)
→ getAnimations().finished → onComplete(true)
CLOSING:
open=false → data-ending-style (element stays in DOM!)
→ getAnimations().finished
→ shouldRender=false + data-ending-style removed → onComplete(false)
```
- `forceMount` mantiene el elemento en DOM siempre (para transiciones CSS)
- `onComplete` usa `getAnimations()` API, no eventos `transitionend` /`animationend`
- Run ID cancellation previene callbacks stale en toggle rapido
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
## 7. Soma class (component runtime scope)
Soma reads `ActiveUix` from context and exposes services to components.
Components import from `$soma` , never from `$active-app` . Nestable: child
`<Soma portalTo="#modals">` overrides parent.
```ts
class Soma {
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
static create(opts?: SomaOptions): Soma; // factory + context set
static get(): Soma | undefined; // safe read
static require(): Soma; // throws if not found
readonly uix: ActiveUix;
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
readonly portalTo: string | HTMLElement | undefined;
// Service accessors (delegate to ActiveUix)
get langs(): ActiveLangs;
get nums(): ActiveNumbers | undefined;
get money(): ActiveCurrency | undefined;
get dates(): ActiveDates | undefined;
get units(): ActiveUnits | undefined;
get prefs(): ActiveUixPrefsView;
get logger(): EngineLogger;
}
```
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
### Service access from components
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
Components access services through Soma, never through App directly:
```ts
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
const soma = Soma.get();
docs: update SOMA_ARCHITECTURE, README, COMPONENT_GUIDE for current state
SOMA_ARCHITECTURE.md:
- §3.4: Soma.getOr() → Soma.get(), removed Soma.translate()
- §3.6: translations via langs.ts() with idlangref, not Soma.translate()
- §6: added Gesture.base/drag/resize + SafePolygon to layers table
- §7: updated service access examples to use idlangref
- §7: rewritten Texto funcional section with langs.ts pattern
- §9: added data-dragging, data-highlighted, data-resizing, --drawer-* vars
- §13: added core/langs.ts, langs.ts in component structure
- §17: checklist synced with COMPONENT_GUIDE (10 steps)
COMPONENT_GUIDE.md:
- A15: exception for pure drag handles (immediate capture)
- A16: added focus return note for non-modal
- A17: virtual focus vs DOM focus strategy
- A18: registry pattern over DOM queries
- A19: SafePolygon for hover-gap components
- A20: exit animation via Presence (dismiss flow)
- A21: contract case normalization
- A22: Dismissal isValidEvent for complex widgets
README.md:
- SafePolygon added to layers table
- Patterns section: registry, virtual focus, exit animation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
soma?.langs.ts('#?common.buttons.close|Close'); // translation via idlangref
soma?.prefs.getDir(); // effective direction from uix.prefs.direction
docs: update SOMA_ARCHITECTURE, README, COMPONENT_GUIDE for current state
SOMA_ARCHITECTURE.md:
- §3.4: Soma.getOr() → Soma.get(), removed Soma.translate()
- §3.6: translations via langs.ts() with idlangref, not Soma.translate()
- §6: added Gesture.base/drag/resize + SafePolygon to layers table
- §7: updated service access examples to use idlangref
- §7: rewritten Texto funcional section with langs.ts pattern
- §9: added data-dragging, data-highlighted, data-resizing, --drawer-* vars
- §13: added core/langs.ts, langs.ts in component structure
- §17: checklist synced with COMPONENT_GUIDE (10 steps)
COMPONENT_GUIDE.md:
- A15: exception for pure drag handles (immediate capture)
- A16: added focus return note for non-modal
- A17: virtual focus vs DOM focus strategy
- A18: registry pattern over DOM queries
- A19: SafePolygon for hover-gap components
- A20: exit animation via Presence (dismiss flow)
- A21: contract case normalization
- A22: Dismissal isValidEvent for complex widgets
README.md:
- SafePolygon added to layers table
- Patterns section: registry, virtual focus, exit animation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
soma?.money?.format(1099); // currency formatting
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates
**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>
6 months ago
soma?.dates?.getDateOrder(); // DMY / MDY / YMD
soma?.dates?.getHourCycle(); // 12 | 24 (numeric — not '12h' / '24h')
docs: update SOMA_ARCHITECTURE, README, COMPONENT_GUIDE for current state
SOMA_ARCHITECTURE.md:
- §3.4: Soma.getOr() → Soma.get(), removed Soma.translate()
- §3.6: translations via langs.ts() with idlangref, not Soma.translate()
- §6: added Gesture.base/drag/resize + SafePolygon to layers table
- §7: updated service access examples to use idlangref
- §7: rewritten Texto funcional section with langs.ts pattern
- §9: added data-dragging, data-highlighted, data-resizing, --drawer-* vars
- §13: added core/langs.ts, langs.ts in component structure
- §17: checklist synced with COMPONENT_GUIDE (10 steps)
COMPONENT_GUIDE.md:
- A15: exception for pure drag handles (immediate capture)
- A16: added focus return note for non-modal
- A17: virtual focus vs DOM focus strategy
- A18: registry pattern over DOM queries
- A19: SafePolygon for hover-gap components
- A20: exit animation via Presence (dismiss flow)
- A21: contract case normalization
- A22: Dismissal isValidEvent for complex widgets
README.md:
- SafePolygon added to layers table
- Patterns section: registry, virtual focus, exit animation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
soma?.portalTo; // portal target
```
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates
**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>
6 months ago
### Date / time types and formatting
Soma imports date-related symbols from `$libs/days` , the canonical date library. Components **never** import from `$lib/util/dates` (legacy) or `@internationalized/date` directly. There is no Soma re-export façade for the date domain.
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates
**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>
6 months ago
- Value types: `CalendarDate` , `CalendarDateTime` , `Time` , `ZonedDateTime`
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
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>
6 months ago
- Types: `DateValue` , `TimeValue` , `DateRange` , `DateMatcher` , `Month` , `WeekStartsOn` , `HourCycle` , `TimeGranularity` , `DateOrder` , `Granularity` , `SegmentPart` , `EditableTimeSegmentPart` , `TimeSegmentObj` , `SegmentValueObj` , `DayPeriod` , …
- Queries: `isSameDay` , `hasTime` , `isZonedDateTime` , `isTimeBefore` , `isTimeAfter` , `today` , `now` , `startOfMonth` , `endOfMonth` , `getLastFirstDayOfWeek` , `getNextLastDayOfWeek` , …
- Operations: `dateValueToDate` , `convertTimeValueToDateValue` , `convertTimeValueToTime` , `toCalendarDate` , `toZoned` , …
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates
**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>
6 months ago
- Parsing: `parseDate` , `parseDateTime` , `parseTime`
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
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>
6 months ago
- Formatting: `DateFormatter` , `getCachedDateFormat` , `getPlaceholder` , `getDefaultDate` , `getDefaultTime` , `inferGranularity` , `inferTimeGranularity` , `getDefaultHourCycle` , `resolveDateOrder(locale)` , `resolveHourCycle(locale)`
- **Segments (dias/segments.ts)**: constants (`DATE_SEGMENT_PARTS`, `EDITABLE_TIME_SEGMENT_PARTS` , …), type guards (`isDateSegmentPart`, `isEditableTimeSegmentPart` , `isDateAndTimeSegmentObj` , …), pure helpers (`initializeSegmentValues`, `initializeTimeSegmentValues` , `getValueFromSegments` , `getTimeValueFromSegments` , `areAllSegmentsFilled` , `createSegmentContent` , `createTimeSegmentContent` , `getOptsByGranularity` , `getOptsByTimeGranularity` ).
soma: dias date library, Calendar + RangeCalendar, Command fixes, doc updates
**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>
6 months ago
`HourCycle` is canonically the numeric form `12 | 24` across the whole framework, matching `Intl.DateTimeFormat` 's `hour12` resolved option. String forms like `'12h'` /`'24h'` are legacy and must not appear in new code.
**`soma/datetime/` holds only UI-level helpers** (screen-reader announcer, DOM segment navigation, `SegmentState` shape with `lastKeyZero` /`hasLeftFocus`/`updating`, `isAcceptableSegmentKey` using KEYS, description-element DOM writers). It must not re-export `$libs/days` symbols — consumers import from `$libs/days` directly. Extending `$libs/days` is the default for new date/time helpers; adding to `soma/datetime/` is only correct when the helper is genuinely UI-specific.
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
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>
6 months ago
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
### Static method convention (project-wide)
All classes that use Svelte context follow the same pattern:
| Method | Returns | Use when |
| ---------------- | ----------------------- | --------------------------------- |
| `X.create(opts)` | instance | Creating + registering in context |
| `X.get()` | instance or `undefined` | Parent/context is optional |
| `X.require()` | instance (throws) | Parent/context is required |
This applies to `App` , `Soma` , and all state classes that use context. No
standalone functions. No `from()` . No `ctx` exposed.
### Texto funcional
Las traducciones propias del componente se declaran en el morfo:
docs: update SOMA_ARCHITECTURE, README, COMPONENT_GUIDE for current state
SOMA_ARCHITECTURE.md:
- §3.4: Soma.getOr() → Soma.get(), removed Soma.translate()
- §3.6: translations via langs.ts() with idlangref, not Soma.translate()
- §6: added Gesture.base/drag/resize + SafePolygon to layers table
- §7: updated service access examples to use idlangref
- §7: rewritten Texto funcional section with langs.ts pattern
- §9: added data-dragging, data-highlighted, data-resizing, --drawer-* vars
- §13: added core/langs.ts, langs.ts in component structure
- §17: checklist synced with COMPONENT_GUIDE (10 steps)
COMPONENT_GUIDE.md:
- A15: exception for pure drag handles (immediate capture)
- A16: added focus return note for non-modal
- A17: virtual focus vs DOM focus strategy
- A18: registry pattern over DOM queries
- A19: SafePolygon for hover-gap components
- A20: exit animation via Presence (dismiss flow)
- A21: contract case normalization
- A22: Dismissal isValidEvent for complex widgets
README.md:
- SafePolygon added to layers table
- Patterns section: registry, virtual focus, exit animation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
```ts
export const drawerMorfo = {
name: 'Drawer',
kebab: 'drawer',
translations: {
trigger: { es: 'Abrir cajon', en: 'Open drawer' }
},
parts: [
{
name: 'Trigger',
kebab: 'trigger',
aria: [{ attr: 'aria-label', value: v.translationRef('trigger', 'Open drawer') }]
}
]
} as const satisfies Morfo;
```
Las traducciones compartidas no se duplican por componente:
docs: update SOMA_ARCHITECTURE, README, COMPONENT_GUIDE for current state
SOMA_ARCHITECTURE.md:
- §3.4: Soma.getOr() → Soma.get(), removed Soma.translate()
- §3.6: translations via langs.ts() with idlangref, not Soma.translate()
- §6: added Gesture.base/drag/resize + SafePolygon to layers table
- §7: updated service access examples to use idlangref
- §7: rewritten Texto funcional section with langs.ts pattern
- §9: added data-dragging, data-highlighted, data-resizing, --drawer-* vars
- §13: added core/langs.ts, langs.ts in component structure
- §17: checklist synced with COMPONENT_GUIDE (10 steps)
COMPONENT_GUIDE.md:
- A15: exception for pure drag handles (immediate capture)
- A16: added focus return note for non-modal
- A17: virtual focus vs DOM focus strategy
- A18: registry pattern over DOM queries
- A19: SafePolygon for hover-gap components
- A20: exit animation via Presence (dismiss flow)
- A21: contract case normalization
- A22: Dismissal isValidEvent for complex widgets
README.md:
- SafePolygon added to layers table
- Patterns section: registry, virtual focus, exit animation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
```ts
value: v.commonRef('buttons.close', 'Close'); // #?common.buttons.close|Close
docs: update SOMA_ARCHITECTURE, README, COMPONENT_GUIDE for current state
SOMA_ARCHITECTURE.md:
- §3.4: Soma.getOr() → Soma.get(), removed Soma.translate()
- §3.6: translations via langs.ts() with idlangref, not Soma.translate()
- §6: added Gesture.base/drag/resize + SafePolygon to layers table
- §7: updated service access examples to use idlangref
- §7: rewritten Texto funcional section with langs.ts pattern
- §9: added data-dragging, data-highlighted, data-resizing, --drawer-* vars
- §13: added core/langs.ts, langs.ts in component structure
- §17: checklist synced with COMPONENT_GUIDE (10 steps)
COMPONENT_GUIDE.md:
- A15: exception for pure drag handles (immediate capture)
- A16: added focus return note for non-modal
- A17: virtual focus vs DOM focus strategy
- A18: registry pattern over DOM queries
- A19: SafePolygon for hover-gap components
- A20: exit animation via Presence (dismiss flow)
- A21: contract case normalization
- A22: Dismissal isValidEvent for complex widgets
README.md:
- SafePolygon added to layers table
- Patterns section: registry, virtual focus, exit animation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
```
`ActiveUix` conecta el registro de morfos con `ActiveLangs` . Cuando el
provider crea `createSomaRuntime(morfo, sources)` o `soma.runtime(morfo,
sources)`, `registerMorfo(morfo)` registra el contrato `data-*` y publica
`morfo.translations` bajo `components.{kebab}` .
`commonLangs` en `core/langs.ts` aporta los defaults de `common.*` .
`ActiveUix` los registra sin pisar hojas existentes, de modo que el
integrador puede pasar sus propias traducciones y UIX solo completa lo que
falte.
No existe catálogo global por componente. `ActiveUix` conecta el registro de
morfos; cada componente publica sus textos cuando su morfo se registra.
## 8. Sistema reactivo
Capa fina sobre runes de Svelte 5 que permite pasar estado reactivo por referencia entre clases.
- `state<T>(initial)` → `State<T>` (mutable, `.current` )
- `readableActive(() => value)` → `Active<T>` (readonly derived)
- `writableActive(getter, setter)` → `State<T>` (two-way binding)
Los wrappers `.svelte` convierten props normales a `Active` /`State` con estas funciones. Esta conversion es la frontera entre el mundo de props de Svelte y el mundo de clases reactivas de soma.
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
## 9. Contratos data-\*
Los `data-*` son API publica formal, validados con `assertContract()` .
Convencion:
```
data-dialog → provider (sin -provider, sin -root)
data-dialog-trigger → parte
data-dialog-content → parte
data-state="open|closed" → estado
data-disabled → flag
data-side="top|right|bottom|left" → posicion flotante
data-align="start|center|end" → alineacion
data-starting-style → animacion de entrada (1 frame)
data-ending-style → animacion de salida (persiste)
data-nested → es hijo de otro del mismo tipo
data-nested-open → tiene un hijo abierto
docs: update SOMA_ARCHITECTURE, README, COMPONENT_GUIDE for current state
SOMA_ARCHITECTURE.md:
- §3.4: Soma.getOr() → Soma.get(), removed Soma.translate()
- §3.6: translations via langs.ts() with idlangref, not Soma.translate()
- §6: added Gesture.base/drag/resize + SafePolygon to layers table
- §7: updated service access examples to use idlangref
- §7: rewritten Texto funcional section with langs.ts pattern
- §9: added data-dragging, data-highlighted, data-resizing, --drawer-* vars
- §13: added core/langs.ts, langs.ts in component structure
- §17: checklist synced with COMPONENT_GUIDE (10 steps)
COMPONENT_GUIDE.md:
- A15: exception for pure drag handles (immediate capture)
- A16: added focus return note for non-modal
- A17: virtual focus vs DOM focus strategy
- A18: registry pattern over DOM queries
- A19: SafePolygon for hover-gap components
- A20: exit animation via Presence (dismiss flow)
- A21: contract case normalization
- A22: Dismissal isValidEvent for complex widgets
README.md:
- SafePolygon added to layers table
- Patterns section: registry, virtual focus, exit animation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
data-dragging → gesture drag activo
data-highlighted → item con virtual focus (aria-activedescendant)
data-resizing → splitter resize activo
```
CSS variables expuestas:
```
--soma-floating-transform-origin
--soma-floating-available-width
--soma-floating-available-height
--soma-floating-anchor-width
--soma-floating-anchor-height
--soma-dialog-depth
--soma-dialog-nested-count
docs: update SOMA_ARCHITECTURE, README, COMPONENT_GUIDE for current state
SOMA_ARCHITECTURE.md:
- §3.4: Soma.getOr() → Soma.get(), removed Soma.translate()
- §3.6: translations via langs.ts() with idlangref, not Soma.translate()
- §6: added Gesture.base/drag/resize + SafePolygon to layers table
- §7: updated service access examples to use idlangref
- §7: rewritten Texto funcional section with langs.ts pattern
- §9: added data-dragging, data-highlighted, data-resizing, --drawer-* vars
- §13: added core/langs.ts, langs.ts in component structure
- §17: checklist synced with COMPONENT_GUIDE (10 steps)
COMPONENT_GUIDE.md:
- A15: exception for pure drag handles (immediate capture)
- A16: added focus return note for non-modal
- A17: virtual focus vs DOM focus strategy
- A18: registry pattern over DOM queries
- A19: SafePolygon for hover-gap components
- A20: exit animation via Presence (dismiss flow)
- A21: contract case normalization
- A22: Dismissal isValidEvent for complex widgets
README.md:
- SafePolygon added to layers table
- Patterns section: registry, virtual focus, exit animation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
--drawer-progress → 0-1 drag progress
--drawer-offset-x / y → drag offset in px
--soma-toast-swipe-move-x / y → toast swipe offset
```
## 10. IDs
Los IDs se generan con contexto de componente:
```
soma-dialog-c12
soma-dialog-trigger-c13
soma-dialog-content-c14
```
Pattern: `soma-{component}-{part}-{uid}` . Descriptivos e inspeccionables.
## 11. Barrel exports
### Componentes (jerárquico)
```ts
// $soma/components/index.ts
export * as Collapsible from './collapsible';
export * as Dialog from './dialog';
export * as Popover from './popover';
```
Consumo:
```ts
import { Dialog, Popover } from '$soma/components';
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
Dialog.Provider; // no SomaDialogProvider, no TerraDialogProvider
Dialog.Trigger;
```
### Internal
```ts
import { Portal, Arrow, VisuallyHidden, Soma } from '$soma/components/internal';
```
## 12. Fronteras externas
`soma` distingue entre:
- internos: `layers/` , `reactive/` , `dom/` , `provider/` — helpers propios
- externos: `@floating-ui/dom` , `runed` , `tabbable` — dependencias npm
Si una dependencia tiene API inestable o podria cambiar, se accede a traves de una frontera formal (como `layers/floating/` wrappea @floating -ui). Las dependencias estables (runed, svelte) se importan directamente.
## 13. Estructura del directorio
```
src/uix/soma/
soma: 12 components, architecture docs, naming fixes, form tier complete
Components (12):
- Accordion (keyboard nav, single/multiple, collapsible, loop)
- Checkbox (indeterminate, group, readonly)
- Collapsible
- Combobox (input + floating listbox, consumer-side filtering)
- Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount)
- DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items)
- Popover (floating, openOnHover, modal, custom anchor)
- RadioGroup (roving tabindex, orientation, readonly)
- Select (single/multiple, typeahead, scroll-into-view, hidden select)
- Slider (drag, keyboard, single/range, ticks, vertical)
- Switch (hidden input, data-checked)
- Tooltip (skip-delay group, 3-state, hover/focus)
Architecture:
- State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts
(avoids Vite module resolution ambiguity with .svelte wrappers)
- Export name standardized: always Provider, never Root
- COMPONENT_GUIDE.md: step-by-step implementation guide with checklist
- SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table
- README.md: updated naming, file structure, inventory
- context.ts: error message uses Provider not Root
- Demo index page at /test/soma/
Bug fixes:
- DropdownMenu trigger missing onclick/onkeydown in props
- DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent)
- Checkbox getContext in event handler → captured in constructor
- Select context not found → was Vite cache issue (not naming)
- ScrollLock unlock path
- Floating wrapper z-index: auto override
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
├── SOMA_ARCHITECTURE.md ← this document
├── COMPONENT_GUIDE.md ← step-by-step implementation guide
├── README.md ← API reference
├── runtime.svelte.ts ← SomaRuntime (morfo interpreter)
├── errors.ts ← typed runtime/context errors
soma: 12 components, architecture docs, naming fixes, form tier complete
Components (12):
- Accordion (keyboard nav, single/multiple, collapsible, loop)
- Checkbox (indeterminate, group, readonly)
- Collapsible
- Combobox (input + floating listbox, consumer-side filtering)
- Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount)
- DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items)
- Popover (floating, openOnHover, modal, custom anchor)
- RadioGroup (roving tabindex, orientation, readonly)
- Select (single/multiple, typeahead, scroll-into-view, hidden select)
- Slider (drag, keyboard, single/range, ticks, vertical)
- Switch (hidden input, data-checked)
- Tooltip (skip-delay group, 3-state, hover/focus)
Architecture:
- State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts
(avoids Vite module resolution ambiguity with .svelte wrappers)
- Export name standardized: always Provider, never Root
- COMPONENT_GUIDE.md: step-by-step implementation guide with checklist
- SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table
- README.md: updated naming, file structure, inventory
- context.ts: error message uses Provider not Root
- Demo index page at /test/soma/
Bug fixes:
- DropdownMenu trigger missing onclick/onkeydown in props
- DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent)
- Checkbox getContext in event handler → captured in constructor
- Select context not found → was Vite cache issue (not naming)
- ScrollLock unlock path
- Floating wrapper z-index: auto override
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
├── core/
docs: update SOMA_ARCHITECTURE, README, COMPONENT_GUIDE for current state
SOMA_ARCHITECTURE.md:
- §3.4: Soma.getOr() → Soma.get(), removed Soma.translate()
- §3.6: translations via langs.ts() with idlangref, not Soma.translate()
- §6: added Gesture.base/drag/resize + SafePolygon to layers table
- §7: updated service access examples to use idlangref
- §7: rewritten Texto funcional section with langs.ts pattern
- §9: added data-dragging, data-highlighted, data-resizing, --drawer-* vars
- §13: added core/langs.ts, langs.ts in component structure
- §17: checklist synced with COMPONENT_GUIDE (10 steps)
COMPONENT_GUIDE.md:
- A15: exception for pure drag handles (immediate capture)
- A16: added focus return note for non-modal
- A17: virtual focus vs DOM focus strategy
- A18: registry pattern over DOM queries
- A19: SafePolygon for hover-gap components
- A20: exit animation via Presence (dismiss flow)
- A21: contract case normalization
- A22: Dismissal isValidEvent for complex widgets
README.md:
- SafePolygon added to layers table
- Patterns section: registry, virtual focus, exit animation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
│ ├── soma.svelte.ts ← Soma class (root instance)
│ └── langs.ts ← shared commonLangs defaults
soma: 12 components, architecture docs, naming fixes, form tier complete
Components (12):
- Accordion (keyboard nav, single/multiple, collapsible, loop)
- Checkbox (indeterminate, group, readonly)
- Collapsible
- Combobox (input + floating listbox, consumer-side filtering)
- Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount)
- DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items)
- Popover (floating, openOnHover, modal, custom anchor)
- RadioGroup (roving tabindex, orientation, readonly)
- Select (single/multiple, typeahead, scroll-into-view, hidden select)
- Slider (drag, keyboard, single/range, ticks, vertical)
- Switch (hidden input, data-checked)
- Tooltip (skip-delay group, 3-state, hover/focus)
Architecture:
- State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts
(avoids Vite module resolution ambiguity with .svelte wrappers)
- Export name standardized: always Provider, never Root
- COMPONENT_GUIDE.md: step-by-step implementation guide with checklist
- SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table
- README.md: updated naming, file structure, inventory
- context.ts: error message uses Provider not Root
- Demo index page at /test/soma/
Bug fixes:
- DropdownMenu trigger missing onclick/onkeydown in props
- DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent)
- Checkbox getContext in event handler → captured in constructor
- Select context not found → was Vite cache issue (not naming)
- ScrollLock unlock path
- Floating wrapper z-index: auto override
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
├── reactive/ ← reactive system
├── provider/ ← context + opts bridge
├── props/ ← mergeProps, composeHandlers
├── keyboard/ ← KEYS, directional
├── dom/ ← DOM utilities, focus
├── css/ ← styleToString, cssToStyleObj
├── id/ ← createId, useId
├── types/ ← shared types + service interfaces
soma: 12 components, architecture docs, naming fixes, form tier complete
Components (12):
- Accordion (keyboard nav, single/multiple, collapsible, loop)
- Checkbox (indeterminate, group, readonly)
- Collapsible
- Combobox (input + floating listbox, consumer-side filtering)
- Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount)
- DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items)
- Popover (floating, openOnHover, modal, custom anchor)
- RadioGroup (roving tabindex, orientation, readonly)
- Select (single/multiple, typeahead, scroll-into-view, hidden select)
- Slider (drag, keyboard, single/range, ticks, vertical)
- Switch (hidden input, data-checked)
- Tooltip (skip-delay group, 3-state, hover/focus)
Architecture:
- State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts
(avoids Vite module resolution ambiguity with .svelte wrappers)
- Export name standardized: always Provider, never Root
- COMPONENT_GUIDE.md: step-by-step implementation guide with checklist
- SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table
- README.md: updated naming, file structure, inventory
- context.ts: error message uses Provider not Root
- Demo index page at /test/soma/
Bug fixes:
- DropdownMenu trigger missing onclick/onkeydown in props
- DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent)
- Checkbox getContext in event handler → captured in constructor
- Select context not found → was Vite cache issue (not naming)
- ScrollLock unlock path
- Floating wrapper z-index: auto override
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
├── layers/ ← behavior layers (classes only)
│ ├── presence.svelte.ts
│ ├── focus-scope.svelte.ts
│ ├── dismissal.svelte.ts
│ ├── text-selection.svelte.ts
│ ├── scroll-lock.svelte.ts
│ ├── resize-observer.svelte.ts
│ └── floating/
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
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>
6 months ago
├── datetime/ ← UI-only helpers (announcer, segment DOM nav,
│ segment UI-state shapes, segment-key predicates,
│ description-element writers). NO date math,
│ NO re-exports of days — import `$libs/days`
│ directly.
├── components/
soma: 12 components, architecture docs, naming fixes, form tier complete
Components (12):
- Accordion (keyboard nav, single/multiple, collapsible, loop)
- Checkbox (indeterminate, group, readonly)
- Collapsible
- Combobox (input + floating listbox, consumer-side filtering)
- Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount)
- DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items)
- Popover (floating, openOnHover, modal, custom anchor)
- RadioGroup (roving tabindex, orientation, readonly)
- Select (single/multiple, typeahead, scroll-into-view, hidden select)
- Slider (drag, keyboard, single/range, ticks, vertical)
- Switch (hidden input, data-checked)
- Tooltip (skip-delay group, 3-state, hover/focus)
Architecture:
- State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts
(avoids Vite module resolution ambiguity with .svelte wrappers)
- Export name standardized: always Provider, never Root
- COMPONENT_GUIDE.md: step-by-step implementation guide with checklist
- SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table
- README.md: updated naming, file structure, inventory
- context.ts: error message uses Provider not Root
- Demo index page at /test/soma/
Bug fixes:
- DropdownMenu trigger missing onclick/onkeydown in props
- DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent)
- Checkbox getContext in event handler → captured in constructor
- Select context not found → was Vite cache issue (not naming)
- ScrollLock unlock path
- Floating wrapper z-index: auto override
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
│ ├── internal/ ← Portal, Arrow, VisuallyHidden, < Soma >
│ ├── {name}/ ← each headless component
│ │ ├── {name}-provider.svelte.ts ← state classes (NOT {name}.svelte.ts)
docs: update SOMA_ARCHITECTURE, README, COMPONENT_GUIDE for current state
SOMA_ARCHITECTURE.md:
- §3.4: Soma.getOr() → Soma.get(), removed Soma.translate()
- §3.6: translations via langs.ts() with idlangref, not Soma.translate()
- §6: added Gesture.base/drag/resize + SafePolygon to layers table
- §7: updated service access examples to use idlangref
- §7: rewritten Texto funcional section with langs.ts pattern
- §9: added data-dragging, data-highlighted, data-resizing, --drawer-* vars
- §13: added core/langs.ts, langs.ts in component structure
- §17: checklist synced with COMPONENT_GUIDE (10 steps)
COMPONENT_GUIDE.md:
- A15: exception for pure drag handles (immediate capture)
- A16: added focus return note for non-modal
- A17: virtual focus vs DOM focus strategy
- A18: registry pattern over DOM queries
- A19: SafePolygon for hover-gap components
- A20: exit animation via Presence (dismiss flow)
- A21: contract case normalization
- A22: Dismissal isValidEvent for complex widgets
README.md:
- SafePolygon added to layers table
- Patterns section: registry, virtual focus, exit animation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
│ │ ├── types.ts ← public props + canonical field shapes
│ │ ├── langs.ts ← optional idlangref constants for imperative strings
soma: 12 components, architecture docs, naming fixes, form tier complete
Components (12):
- Accordion (keyboard nav, single/multiple, collapsible, loop)
- Checkbox (indeterminate, group, readonly)
- Collapsible
- Combobox (input + floating listbox, consumer-side filtering)
- Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount)
- DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items)
- Popover (floating, openOnHover, modal, custom anchor)
- RadioGroup (roving tabindex, orientation, readonly)
- Select (single/multiple, typeahead, scroll-into-view, hidden select)
- Slider (drag, keyboard, single/range, ticks, vertical)
- Switch (hidden input, data-checked)
- Tooltip (skip-delay group, 3-state, hover/focus)
Architecture:
- State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts
(avoids Vite module resolution ambiguity with .svelte wrappers)
- Export name standardized: always Provider, never Root
- COMPONENT_GUIDE.md: step-by-step implementation guide with checklist
- SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table
- README.md: updated naming, file structure, inventory
- context.ts: error message uses Provider not Root
- Demo index page at /test/soma/
Bug fixes:
- DropdownMenu trigger missing onclick/onkeydown in props
- DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent)
- Checkbox getContext in event handler → captured in constructor
- Select context not found → was Vite cache issue (not naming)
- ScrollLock unlock path
- Floating wrapper z-index: auto override
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
│ │ ├── exports.ts
│ │ ├── index.ts
│ │ └── components/
│ │ ├── {name}.svelte ← root wrapper
│ │ ├── {name}-trigger.svelte
│ │ └── ...
│ └── index.ts ← hierarchical barrel
└── index.ts ← main barrel
```
soma: 12 components, architecture docs, naming fixes, form tier complete
Components (12):
- Accordion (keyboard nav, single/multiple, collapsible, loop)
- Checkbox (indeterminate, group, readonly)
- Collapsible
- Combobox (input + floating listbox, consumer-side filtering)
- Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount)
- DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items)
- Popover (floating, openOnHover, modal, custom anchor)
- RadioGroup (roving tabindex, orientation, readonly)
- Select (single/multiple, typeahead, scroll-into-view, hidden select)
- Slider (drag, keyboard, single/range, ticks, vertical)
- Switch (hidden input, data-checked)
- Tooltip (skip-delay group, 3-state, hover/focus)
Architecture:
- State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts
(avoids Vite module resolution ambiguity with .svelte wrappers)
- Export name standardized: always Provider, never Root
- COMPONENT_GUIDE.md: step-by-step implementation guide with checklist
- SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table
- README.md: updated naming, file structure, inventory
- context.ts: error message uses Provider not Root
- Demo index page at /test/soma/
Bug fixes:
- DropdownMenu trigger missing onclick/onkeydown in props
- DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent)
- Checkbox getContext in event handler → captured in constructor
- Select context not found → was Vite cache issue (not naming)
- ScrollLock unlock path
- Floating wrapper z-index: auto override
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
### File naming convention
- State class: `{name}-provider.svelte.ts` — NOT `{name}.svelte.ts`
- Avoids Vite module resolution ambiguity with `{name}.svelte` wrapper
- Reflects what's inside: provider/state classes
soma: 12 components, architecture docs, naming fixes, form tier complete
Components (12):
- Accordion (keyboard nav, single/multiple, collapsible, loop)
- Checkbox (indeterminate, group, readonly)
- Collapsible
- Combobox (input + floating listbox, consumer-side filtering)
- Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount)
- DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items)
- Popover (floating, openOnHover, modal, custom anchor)
- RadioGroup (roving tabindex, orientation, readonly)
- Select (single/multiple, typeahead, scroll-into-view, hidden select)
- Slider (drag, keyboard, single/range, ticks, vertical)
- Switch (hidden input, data-checked)
- Tooltip (skip-delay group, 3-state, hover/focus)
Architecture:
- State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts
(avoids Vite module resolution ambiguity with .svelte wrappers)
- Export name standardized: always Provider, never Root
- COMPONENT_GUIDE.md: step-by-step implementation guide with checklist
- SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table
- README.md: updated naming, file structure, inventory
- context.ts: error message uses Provider not Root
- Demo index page at /test/soma/
Bug fixes:
- DropdownMenu trigger missing onclick/onkeydown in props
- DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent)
- Checkbox getContext in event handler → captured in constructor
- Select context not found → was Vite cache issue (not naming)
- ScrollLock unlock path
- Floating wrapper z-index: auto override
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
- Root wrapper: `{name}.svelte` in `components/` subdirectory
- Export name: always `Provider` , never `Root`
## 14. Anti-patterns
Avoid in soma:
- Complex logic inside wrapper `.svelte` — belongs in Provider
- Props drilling when context is the correct pattern
- `data-*` attrs outside of contract
- Inventing part names without checking reference library anatomies (ark-ui, bits-ui, radix-ui)
- Nesting layers as component wrappers in templates
- Inline `z-index: auto` that overrides CSS
- Coupling primitives to app libraries
- Product copy inside the primitive
- Speculative abstractions ("just in case")
- One-line files that only re-export (merge into parent)
- Redundant naming prefixes (SomaDialog, DialogLayerState)
- Dummy refs to satisfy a type — use `ProviderOpts` for no-DOM roots
- **State class file named same as wrapper** — `select.svelte.ts` + `components/select.svelte` causes Vite module duplication. Always use `{name}-provider.svelte.ts`
- **Event handlers not in props** — defining onclick as a class method but not including it in the props derived object
- **getContext in event handlers** — getContext only works during initialization. Capture references in constructor
- **Exporting as Root** — always `Provider` , never `Root`
- **Skipping reference library comparison** — mandatory step, no exceptions
- **Comments in Spanish** — all code comments in English
soma: gesture layer, drawer component, full audit fixes across 26 components
Gesture Layer:
- Gesture.base(), Gesture.drag(), Gesture.resize() — shared pointer tracking
- VelocityTracker with ring buffer, staleness check, projection
- 52 tests (velocity + gesture integration)
- setPointerCapture deferred until moveBuffer exceeded (prevents click stealing)
Drawer Component:
- 8 parts: Provider, Trigger, Content, Overlay, Handle, Title, Description, Close
- Gesture.drag() integration for swipe dismiss
- Snap points (fractional + px), fadeFromIndex, handle tap-to-cycle
- Modal/non-modal/non-dismissible modes
- Logical directions (start/end) RTL-aware via soma presentation
- CSS custom properties (--drawer-progress, --drawer-offset-x/y)
Architecture:
- T1: 219 files migrated from $soma/ alias to relative paths
- T2: 35 wrappers — noop replaced with () => {} inline
- Translation system: componentLangs + common.buttons.* + idlangref constants
- COMPONENT_GUIDE.md + README.md fully updated to reflect current patterns
- contracts.ts: case-normalized registerContract/assertContract
Audit Fixes (P0 critical):
- DI1/PO1/TO1: 'provider' → 'root' in parts/contract/super
- TB1: data-table attr on wrapper
- CH1/SW1/NF4: hidden input rendering fixes
- SP2: panel cleanup on unmount
- RG1: roving tabindex fallback to first item
- CO1: aria-labelledby on collapsible content
Audit Fixes (P1 high):
- SafePolygon integrated in dropdown-menu SubProvider + tooltip
- 9 langs.ts files with idlangref constants
- Dialog: aria-label removed (WCAG 2.5.3), handleOpen/Close guards
- Stepper: count decrement + private, aria-label on List
- ToggleGroup: roving tabindex in multiple mode
- Select: pure aria-activedescendant (no mixed focus), label registry
- NumberField: startValue respects min, scrubber commit on pointerup only
- Toolbar: element-based focus tracking (not index)
Audit Fixes (P2 medium):
- A6: timer/listener cleanup in 6 components
- A12: getDirectionalKeys in 4 components
- A10: DOM queries scoped to root in 3 components
- Dropdown-menu: typeahead + Home/End in SubContent, activate() pattern
- Checkbox: PrimitiveLabelAttributes, getAriaChecked helper, stale value cleanup
- Pagination: reactive page clamping, Set-based computePageRange
- Scroll-area: thumbSize zero-guard, mount flash prevention
- Splitter: collapse/expand respects adjacent constraints, immediate pointer capture
5 issues remain (design decisions): stepper registry, table sub-row IDs,
tags-input ARIA model, tags-input paste, toast exit animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
- **Standalone context functions** — no `createX()` , `getX()` , `useX()` as loose functions. Use `X.create()` , `X.get()` , `X.require()` static methods
- **Importing from `$lib/ext/app` ** in components — components access services through `Soma` , never App directly
- **`from()` as factory name** — use `create()` consistently
- **Re-implementing date/time helpers inside soma** — extend `$libs/days` (A23). Importing from `$lib/util/dates` (legacy vendored) or `@internationalized/date` directly is forbidden; use `$libs/days` .
- **Re-export façades over days** — a soma module whose only job is to forward `$libs/days` symbols is dead weight. Consumers import from `$libs/days` directly.
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
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>
6 months ago
- **UI-level helpers in dias, or date math in `soma/datetime/` ** — dias is pure (no DOM, no Svelte, no KEYS); `soma/datetime/` is UI-only (screen-reader announcer, DOM segment navigation, `SegmentState` shapes, KEYS-based predicates). No crossover
- **`readonlySegments` without a concrete value anchor** — A24: warn via `soma?.logger.warn` when `value` is undefined. Range components split into `startReadonlySegments` / `endReadonlySegments` (A25)
- **`keydown.preventDefault()` as the only guard on contenteditable segments** — IME/paste/drop bypass keydown. Always add `onbeforeinput: e => e.preventDefault()` (A26)
- **Time placeholders as `'– – '` ** — use `createSegmentContent` / `createTimeSegmentContent` from `dias/segments.ts` ; time parts render as `hh` /`mm`/`ss` (A28)
- **Pickers that reimplement field/calendar/popover** — compose via shared `writableActive` refs (A27). Only `Provider` , `Trigger` , and the calendar/slider bridge are unique parts
- **Demo pages as galleries of canned snippets** — every soma demo must be an interactive testbed wiring every public prop to a live control, including a Field-integration section (A29)
- **`HourCycle` as `'12h' \| '24h'` ** — canonical form is numeric `12 \| 24` (matches `Intl.DateTimeFormat.hour12` ). String forms are legacy
soma: 12 components, architecture docs, naming fixes, form tier complete
Components (12):
- Accordion (keyboard nav, single/multiple, collapsible, loop)
- Checkbox (indeterminate, group, readonly)
- Collapsible
- Combobox (input + floating listbox, consumer-side filtering)
- Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount)
- DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items)
- Popover (floating, openOnHover, modal, custom anchor)
- RadioGroup (roving tabindex, orientation, readonly)
- Select (single/multiple, typeahead, scroll-into-view, hidden select)
- Slider (drag, keyboard, single/range, ticks, vertical)
- Switch (hidden input, data-checked)
- Tooltip (skip-delay group, 3-state, hover/focus)
Architecture:
- State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts
(avoids Vite module resolution ambiguity with .svelte wrappers)
- Export name standardized: always Provider, never Root
- COMPONENT_GUIDE.md: step-by-step implementation guide with checklist
- SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table
- README.md: updated naming, file structure, inventory
- context.ts: error message uses Provider not Root
- Demo index page at /test/soma/
Bug fixes:
- DropdownMenu trigger missing onclick/onkeydown in props
- DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent)
- Checkbox getContext in event handler → captured in constructor
- Select context not found → was Vite cache issue (not naming)
- ScrollLock unlock path
- Floating wrapper z-index: auto override
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
docs: prune obsolete audits + studies, refresh remaining references
Deleted 60 obsolete documents that no longer reflect the active-uix
state:
- 47 per-component audit logs (*-audit.md, audit_N.md, AUDIT.md)
- 5 global soma audits (AUDIT_1, codex_audit, soma-audit-04-20/21,
audit-prompt template)
- 5 closed studies (soma/study, morfo/study, morfo/DESIGN, morfo/
PROVIDER_STUDY_2026-04-23, uix/CONTINUITY_2026-04-24)
- 2 superseded refactor proposals (refactor_claude, refactor_code)
- 1 stale bug log (form/BUG-onchange-onblur-hang — repro path
src/routes/test/sium/kitchen-sink no longer exists)
Updated the remaining operational references to drop dead links and
reflect the post-cleanup architecture:
- src/uix/README.md — rewritten links section, dropped pointers to
terra/air/CONTINUITY/refactor docs, updated adom/dom paths
- src/uix/active_architecture.md — header now dated 2026-05-08, lists
the major shifts since 2026-04-26 (provider inheritance dropped,
eidos in production, doctrina parte IV, sound eager-init); pendings
section reflects the eidos migration roadmap; "see also" purged of
deleted files
- src/uix/morfo/README.md — file tree updated (removed DESIGN/study,
added compile/runtime/PERMUTATION_RUNNER); see-also pruned
- src/uix/sema/README.md — SoundChannel section now describes the
eager-init pattern (constructor-time, not lazy); pointer to
sema-implementation-guide added
- src/uix/soma/README.md — drops terra/air mentions, declares the
actual dependency direction (eidos -> soma, not the reverse)
- src/uix/soma/SOMA_ARCHITECTURE.md — §15 rewritten as "Estado actual
y deuda histórica" reflecting Provider-inheritance-drop; demo path
updated to web/routes/
- src/uix/soma/COMPONENT_GUIDE.md — A23 dates section now points to
$libs/days (no external/dates façade); demo paths swapped from
/test/soma/* to web/routes/
- src/uix/morfo/PERMUTATION_RUNNER.md — paths swapped to web/routes/
with a relocation note
- CLAUDE.md — Refactor Documents section replaced with Reference
Documents pointing to per-layer READMEs
npm run check holds at baseline (39 pre-existing errors, none new).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
## 15. Estado actual y deuda histórica
Soma ha pasado por varias fases. La forma actual (rama `active-uix` ,
post-2026-05-08):
- **Provider inheritance dropped** — los providers ya no heredan de un
base abstracto; son clases concretas. La mecanica DOM comun vive en
`SomaRuntime.part(...)` , y los casos que necesitan eventos semanticos
usan el mismo `SomaRuntime` para `trigger` /`keydown`.
soma/morfo/active-uix: relocate runtime + rename SomaRuntime + sema opcional
Architectural correction. Morfo was importing Svelte runes, $adom, and
$uix/sema — all violations of "morfo is pure declarative DNA". The
runtime that interprets a CompiledMorfo lives where it executes: soma.
Move:
- src/uix/morfo/runtime.svelte.ts → src/uix/soma/runtime.svelte.ts
- src/uix/morfo/runtime.svelte.test.ts → src/uix/soma/runtime.svelte.test.ts
Rename (consistent with the move; "soma's runtime", per the doctrine
"morfo declares, soma executes"):
- class MorfoRuntime → SomaRuntime
- createMorfoRuntime() → createSomaRuntime()
- type MorfoRuntimeSources → SomaRuntimeSources
- error prefixes [morfo-runtime] → [soma-runtime]
Sema is now genuinely ornamental:
- ActiveUix.semantic returns EngineSemantic | undefined (no longer
throws when no engine is configured). Components in SSR / headless
tests / audio-disabled environments stay functional.
- SomaRuntime.trigger() skips the emit step AND the target-resolution
check when no engine is present — only requires a DOM target when
something will actually be emitted.
- Soma.semantic getter returns | undefined too (was non-optional).
Consumers updated:
- src/uix/active-uix/active-uix.svelte.ts — imports createSomaRuntime
from $soma; semantic getter returns | undefined
- src/uix/active-uix/types.ts — interface SomaRuntime, semantic | undefined
- 8 providers (toggle, switch, collapsible, dialog, drawer, popover,
toast, avatar) — type SomaRuntime, import path $soma
- src/uix/soma/index.ts — re-exports createSomaRuntime + types
- src/uix/morfo/index.ts — drops the runtime re-exports (morfo is pure)
- src/uix/sema/signal.ts — JSDoc reference updated
- src/uix/soma/core/soma.svelte.ts — semantic getter | undefined
Test added: SomaRuntime.trigger() succeeds without a semantic engine
(skips emit + target check, runs handler directly).
Docs aligned to the new shape:
- active_architecture.md — runtime locations, sema as ornamental
- soma/SOMA_ARCHITECTURE.md, soma/README.md — name + import paths
- morfo/README.md — file listing now flags morfo as PURE TS, points
at soma/runtime.svelte.ts for the interpreter
- sema/README.md — SomaRuntime references
- README.md — root architecture diagram
- CLAUDE.md — hand-off note
Tests 218/218 green. npm run check holds at the 39-error baseline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
- **SomaRuntime cachea** la compilación del morfo (`compileMorfo` por
docs: prune obsolete audits + studies, refresh remaining references
Deleted 60 obsolete documents that no longer reflect the active-uix
state:
- 47 per-component audit logs (*-audit.md, audit_N.md, AUDIT.md)
- 5 global soma audits (AUDIT_1, codex_audit, soma-audit-04-20/21,
audit-prompt template)
- 5 closed studies (soma/study, morfo/study, morfo/DESIGN, morfo/
PROVIDER_STUDY_2026-04-23, uix/CONTINUITY_2026-04-24)
- 2 superseded refactor proposals (refactor_claude, refactor_code)
- 1 stale bug log (form/BUG-onchange-onblur-hang — repro path
src/routes/test/sium/kitchen-sink no longer exists)
Updated the remaining operational references to drop dead links and
reflect the post-cleanup architecture:
- src/uix/README.md — rewritten links section, dropped pointers to
terra/air/CONTINUITY/refactor docs, updated adom/dom paths
- src/uix/active_architecture.md — header now dated 2026-05-08, lists
the major shifts since 2026-04-26 (provider inheritance dropped,
eidos in production, doctrina parte IV, sound eager-init); pendings
section reflects the eidos migration roadmap; "see also" purged of
deleted files
- src/uix/morfo/README.md — file tree updated (removed DESIGN/study,
added compile/runtime/PERMUTATION_RUNNER); see-also pruned
- src/uix/sema/README.md — SoundChannel section now describes the
eager-init pattern (constructor-time, not lazy); pointer to
sema-implementation-guide added
- src/uix/soma/README.md — drops terra/air mentions, declares the
actual dependency direction (eidos -> soma, not the reverse)
- src/uix/soma/SOMA_ARCHITECTURE.md — §15 rewritten as "Estado actual
y deuda histórica" reflecting Provider-inheritance-drop; demo path
updated to web/routes/
- src/uix/soma/COMPONENT_GUIDE.md — A23 dates section now points to
$libs/days (no external/dates façade); demo paths swapped from
/test/soma/* to web/routes/
- src/uix/morfo/PERMUTATION_RUNNER.md — paths swapped to web/routes/
with a relocation note
- CLAUDE.md — Refactor Documents section replaced with Reference
Documents pointing to per-layer READMEs
npm run check holds at baseline (39 pre-existing errors, none new).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
WeakMap) y registra los `effects` que sincronizan `state → attrs` vía
`dom.apply` .
- **Naming**: `Provider` (nunca `Root` ); child providers referencian al
padre como `provider` , nunca `root` . El export de un componente
multi-parte sigue la forma compound `Toggle.Provider + Toggle.Trigger
- ...`.
docs: prune obsolete audits + studies, refresh remaining references
Deleted 60 obsolete documents that no longer reflect the active-uix
state:
- 47 per-component audit logs (*-audit.md, audit_N.md, AUDIT.md)
- 5 global soma audits (AUDIT_1, codex_audit, soma-audit-04-20/21,
audit-prompt template)
- 5 closed studies (soma/study, morfo/study, morfo/DESIGN, morfo/
PROVIDER_STUDY_2026-04-23, uix/CONTINUITY_2026-04-24)
- 2 superseded refactor proposals (refactor_claude, refactor_code)
- 1 stale bug log (form/BUG-onchange-onblur-hang — repro path
src/routes/test/sium/kitchen-sink no longer exists)
Updated the remaining operational references to drop dead links and
reflect the post-cleanup architecture:
- src/uix/README.md — rewritten links section, dropped pointers to
terra/air/CONTINUITY/refactor docs, updated adom/dom paths
- src/uix/active_architecture.md — header now dated 2026-05-08, lists
the major shifts since 2026-04-26 (provider inheritance dropped,
eidos in production, doctrina parte IV, sound eager-init); pendings
section reflects the eidos migration roadmap; "see also" purged of
deleted files
- src/uix/morfo/README.md — file tree updated (removed DESIGN/study,
added compile/runtime/PERMUTATION_RUNNER); see-also pruned
- src/uix/sema/README.md — SoundChannel section now describes the
eager-init pattern (constructor-time, not lazy); pointer to
sema-implementation-guide added
- src/uix/soma/README.md — drops terra/air mentions, declares the
actual dependency direction (eidos -> soma, not the reverse)
- src/uix/soma/SOMA_ARCHITECTURE.md — §15 rewritten as "Estado actual
y deuda histórica" reflecting Provider-inheritance-drop; demo path
updated to web/routes/
- src/uix/soma/COMPONENT_GUIDE.md — A23 dates section now points to
$libs/days (no external/dates façade); demo paths swapped from
/test/soma/* to web/routes/
- src/uix/morfo/PERMUTATION_RUNNER.md — paths swapped to web/routes/
with a relocation note
- CLAUDE.md — Refactor Documents section replaced with Reference
Documents pointing to per-layer READMEs
npm run check holds at baseline (39 pre-existing errors, none new).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
- **Data-attr naming**: `data-{component}` (provider) y
`data-{component}-{kebab}` (sub-parts). Sin prefijo `data-soma-*` . El
compiler emite estos via `compiled.parts.attrs` .
- **State files**: `{name}-provider.svelte.ts` (explicit, no ambiguity).
- **IDs**: descriptive (`soma-dialog-trigger-c13`).
## 16. Regla de estabilidad
Un componente de soma se considera estable cuando:
- su API publica esta clara y documentada con JSDoc
- sus `data-*` estan registrados y validados con `assertContract`
- el wrapper y el Provider siguen el patron general
- su accesibilidad base esta resuelta (ARIA, roles, keyboard)
- sus props se han comparado con ark-ui, bits-ui y radix-ui
docs: prune obsolete audits + studies, refresh remaining references
Deleted 60 obsolete documents that no longer reflect the active-uix
state:
- 47 per-component audit logs (*-audit.md, audit_N.md, AUDIT.md)
- 5 global soma audits (AUDIT_1, codex_audit, soma-audit-04-20/21,
audit-prompt template)
- 5 closed studies (soma/study, morfo/study, morfo/DESIGN, morfo/
PROVIDER_STUDY_2026-04-23, uix/CONTINUITY_2026-04-24)
- 2 superseded refactor proposals (refactor_claude, refactor_code)
- 1 stale bug log (form/BUG-onchange-onblur-hang — repro path
src/routes/test/sium/kitchen-sink no longer exists)
Updated the remaining operational references to drop dead links and
reflect the post-cleanup architecture:
- src/uix/README.md — rewritten links section, dropped pointers to
terra/air/CONTINUITY/refactor docs, updated adom/dom paths
- src/uix/active_architecture.md — header now dated 2026-05-08, lists
the major shifts since 2026-04-26 (provider inheritance dropped,
eidos in production, doctrina parte IV, sound eager-init); pendings
section reflects the eidos migration roadmap; "see also" purged of
deleted files
- src/uix/morfo/README.md — file tree updated (removed DESIGN/study,
added compile/runtime/PERMUTATION_RUNNER); see-also pruned
- src/uix/sema/README.md — SoundChannel section now describes the
eager-init pattern (constructor-time, not lazy); pointer to
sema-implementation-guide added
- src/uix/soma/README.md — drops terra/air mentions, declares the
actual dependency direction (eidos -> soma, not the reverse)
- src/uix/soma/SOMA_ARCHITECTURE.md — §15 rewritten as "Estado actual
y deuda histórica" reflecting Provider-inheritance-drop; demo path
updated to web/routes/
- src/uix/soma/COMPONENT_GUIDE.md — A23 dates section now points to
$libs/days (no external/dates façade); demo paths swapped from
/test/soma/* to web/routes/
- src/uix/morfo/PERMUTATION_RUNNER.md — paths swapped to web/routes/
with a relocation note
- CLAUDE.md — Refactor Documents section replaced with Reference
Documents pointing to per-layer READMEs
npm run check holds at baseline (39 pre-existing errors, none new).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
- tiene demo page funcional en `web/routes/{componente}/`
- no depende de hacks locales, z-index hardcodeados, ni demo CSS para sostenerse
- compila con 0 errores (`svelte-check`)
soma: 12 components, architecture docs, naming fixes, form tier complete
Components (12):
- Accordion (keyboard nav, single/multiple, collapsible, loop)
- Checkbox (indeterminate, group, readonly)
- Collapsible
- Combobox (input + floating listbox, consumer-side filtering)
- Dialog (variant dialog/alertdialog, modal, nested, animations, forceMount)
- DropdownMenu (15 parts, submenus, typeahead, checkbox/radio items)
- Popover (floating, openOnHover, modal, custom anchor)
- RadioGroup (roving tabindex, orientation, readonly)
- Select (single/multiple, typeahead, scroll-into-view, hidden select)
- Slider (drag, keyboard, single/range, ticks, vertical)
- Switch (hidden input, data-checked)
- Tooltip (skip-delay group, 3-state, hover/focus)
Architecture:
- State class files renamed: {name}.svelte.ts → {name}-provider.svelte.ts
(avoids Vite module resolution ambiguity with .svelte wrappers)
- Export name standardized: always Provider, never Root
- COMPONENT_GUIDE.md: step-by-step implementation guide with checklist
- SOMA_ARCHITECTURE.md: updated structure, anti-patterns, improvements table
- README.md: updated naming, file structure, inventory
- context.ts: error message uses Provider not Root
- Demo index page at /test/soma/
Bug fixes:
- DropdownMenu trigger missing onclick/onkeydown in props
- DropdownMenu interactOutsideBehavior always 'close' (not modal-dependent)
- Checkbox getContext in event handler → captured in constructor
- Select context not found → was Vite cache issue (not naming)
- ScrollLock unlock path
- Floating wrapper z-index: auto override
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
## 17. New component checklist
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
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>
6 months ago
See `COMPONENT_GUIDE.md` for the full step-by-step process (27 general steps + 4 date/time specific, with rules A1– A29). Summary:
```
docs: update SOMA_ARCHITECTURE, README, COMPONENT_GUIDE for current state
SOMA_ARCHITECTURE.md:
- §3.4: Soma.getOr() → Soma.get(), removed Soma.translate()
- §3.6: translations via langs.ts() with idlangref, not Soma.translate()
- §6: added Gesture.base/drag/resize + SafePolygon to layers table
- §7: updated service access examples to use idlangref
- §7: rewritten Texto funcional section with langs.ts pattern
- §9: added data-dragging, data-highlighted, data-resizing, --drawer-* vars
- §13: added core/langs.ts, langs.ts in component structure
- §17: checklist synced with COMPONENT_GUIDE (10 steps)
COMPONENT_GUIDE.md:
- A15: exception for pure drag handles (immediate capture)
- A16: added focus return note for non-modal
- A17: virtual focus vs DOM focus strategy
- A18: registry pattern over DOM queries
- A19: SafePolygon for hover-gap components
- A20: exit animation via Presence (dismiss flow)
- A21: contract case normalization
- A22: Dismissal isValidEvent for complex widgets
README.md:
- SafePolygon added to layers table
- Patterns section: registry, virtual focus, exit animation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
[ ] 1. Compare with ark-ui, bits-ui, radix-ui — feature table
[ ] 2. Verify membership criteria
[ ] 3. Define parts + attrs + morfo.translations when the component owns text
docs: update SOMA_ARCHITECTURE, README, COMPONENT_GUIDE for current state
SOMA_ARCHITECTURE.md:
- §3.4: Soma.getOr() → Soma.get(), removed Soma.translate()
- §3.6: translations via langs.ts() with idlangref, not Soma.translate()
- §6: added Gesture.base/drag/resize + SafePolygon to layers table
- §7: updated service access examples to use idlangref
- §7: rewritten Texto funcional section with langs.ts pattern
- §9: added data-dragging, data-highlighted, data-resizing, --drawer-* vars
- §13: added core/langs.ts, langs.ts in component structure
- §17: checklist synced with COMPONENT_GUIDE (10 steps)
COMPONENT_GUIDE.md:
- A15: exception for pure drag handles (immediate capture)
- A16: added focus return note for non-modal
- A17: virtual focus vs DOM focus strategy
- A18: registry pattern over DOM queries
- A19: SafePolygon for hover-gap components
- A20: exit animation via Presence (dismiss flow)
- A21: contract case normalization
- A22: Dismissal isValidEvent for complex widgets
README.md:
- SafePolygon added to layers table
- Patterns section: registry, virtual focus, exit animation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
[ ] 4. Create types.ts (props + canonical field shapes)
[ ] 5. Create langs.ts only for imperative idlangref constants, not as the catalog
[ ] 6. Create {name}-provider.svelte.ts (concrete state classes, no Provider inheritance)
docs: update SOMA_ARCHITECTURE, README, COMPONENT_GUIDE for current state
SOMA_ARCHITECTURE.md:
- §3.4: Soma.getOr() → Soma.get(), removed Soma.translate()
- §3.6: translations via langs.ts() with idlangref, not Soma.translate()
- §6: added Gesture.base/drag/resize + SafePolygon to layers table
- §7: updated service access examples to use idlangref
- §7: rewritten Texto funcional section with langs.ts pattern
- §9: added data-dragging, data-highlighted, data-resizing, --drawer-* vars
- §13: added core/langs.ts, langs.ts in component structure
- §17: checklist synced with COMPONENT_GUIDE (10 steps)
COMPONENT_GUIDE.md:
- A15: exception for pure drag handles (immediate capture)
- A16: added focus return note for non-modal
- A17: virtual focus vs DOM focus strategy
- A18: registry pattern over DOM queries
- A19: SafePolygon for hover-gap components
- A20: exit animation via Presence (dismiss flow)
- A21: contract case normalization
- A22: Dismissal isValidEvent for complex widgets
README.md:
- SafePolygon added to layers table
- Patterns section: registry, virtual focus, exit animation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6 months ago
[ ] 7. Create wrapper .svelte files (thin)
[ ] 8. Create exports.ts + index.ts
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
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>
6 months ago
[ ] 9. Create interactive demo page + link in index (A29)
[ ] 10. README.md with anatomy, ARIA, data-attrs, comparison table
[ ] 11. svelte-check + test in browser
[ ] 12. Date/time components: only consume date/time domain via `$libs/days` ,
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
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>
6 months ago
`onbeforeinput` on contenteditable, readonly-without-value warning,
picker composition pattern (A23– A28)
```