eidos: V1 minimum viable — tokens + archetype rules + event reactions
First eidos code. Closes the cross-layer cycle that's been documented but
unproven: an actual CSS layer that consumes the morfo + sema contracts
and applies styles transversally without enumerating components.
src/uix/eidos/
- tokens.css — motion durations (quick/fast/base) + easings (standard /
emphasized / decelerate) + opacity scale (hover/disabled/faded) +
intent tints (neutral/affirm/fulfill/risk/threat) + focus ring tokens.
~30 lines. No theming dimension yet — when it arrives, these are the
base theme.
- archetypes.css — transversal selectors keyed by `data-archetype`.
Covers trigger, overlay, content, indicator, thumb, track, close,
action, item, option. Plus a universal `:focus-visible` ring rule that
applies to every archetype-classified element. Single attribute
selector specificity — components can override freely.
- events.css — reactions to perceptual signals during their ~13ms life.
`[data-event^="dismiss"]` fades out, `[data-event^="announce"]` pulses
with intent-tinted box-shadow, `[data-last-action]` tints exit
animation by causa.
- index.css — entrypoint that imports the above in cascade order.
Consumer: `import '$uix/eidos/index.css'`.
src/routes/test/eidos/+page.svelte (demo)
- Sets up Soma + App context inline (independent of /test/soma layout).
- Imports `$uix/eidos/index.css` and demonstrates the cross-layer story:
- Toggle + Switch buttons share trigger styles via `[data-archetype="trigger"]`
without per-component CSS.
- Collapsible expands/collapses with state-driven attrs.
- Toast intent buttons fire `data-event="announce" data-intent="risk|threat|affirm"`,
which eidos's events.css briefly tints via box-shadow during the signal window.
- Component-specific layout (positioning, switch thumb transform) lives in
the page's `<style>` block — eidos provides only the cross-component
baseline, NOT per-component recipes.
What this proves
- The runtime emits `data-archetype` correctly in browser (not just in
unit tests).
- A single CSS rule can target ALL triggers across all components
without enumeration. Add a new component with `archetype: 'trigger'`
and it inherits the styling automatically.
- Sema's `data-event*` signals live in the DOM long enough for CSS to
react (the ~13ms window measured by `scripts/probe-toast-dismiss.ts`
is observable as a brief box-shadow flash on toasts).
- The cross-layer cycle Morfo declara → Soma transcribe → Sema emite →
Dom aplica → Eidos lee runs end-to-end.
What V1 deliberately excludes
- Theming dimension (no `[data-theme]` overrides yet).
- Per-component recipes (each component will get its own CSS later).
- Keyframes / @starting-style / animation primitives.
- Sound / vibra (those are separate engines that subscribe to sema).
Verification
- /test/eidos returns 200; manual browser check shows triggers share
hover dim + focus ring across components, toasts flash intent tint on
appear.
- 68/68 smoke routes (smoke only scans /test/soma — eidos route is
outside that scope, manually verified).
- svelte-check unchanged at 155 (pure CSS additions).
6 months ago
|
|
|
/**
|
eidos: phase 1 — translate air's foundation (contracts, tokens, themes/base)
Translates the foundation of air's visual system into eidos:
- eidos/contracts/ public token interfaces (4 root + 64 components)
- eidos/tokens/ per-component token values (62 components + motion)
- eidos/themes/base/ light + dark + _static + motion (4 files)
Mechanical translation: `--air-*` → `--eidos-*` everywhere, relative
imports preserved. The CSS is currently NOT wired into the legacy air/
folder, which stays for reference until phase 4 cleanup.
The `--eidos-*` prefix is documented in eidos/index.css with the two
reasons it earns its keep:
1. Authorship clarity at debug time (DevTools can identify the layer)
2. Override discipline (consumers know they're targeting public API)
Phase 1 only adds the foundation without touching component recipes.
The hand-rolled recipes (toast, dialog, toggle, switch, collapsible)
and the legacy hand-rolled tokens.css continue to load alongside the
new tree. Phase 2 will replace recipes with air-translated equivalents
and reconcile the duplicate motion/tint tokens.
Architectural note: air targeted terra and gated its CSS with
`.air-{component}` class on a wrapper. Eidos targets soma+morfo and
applies universally to the data-attrs the morfo declares — no class
gates, no Svelte wrappers. The morfo layer is the one that decides
which `data-*` attrs exist, which is the substantive shift between
the two systems.
Verification: smoke 68/68, morfo-check 66/66, focus rings + dialog
entrance/exit still working per Playwright check (curve unchanged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
* Eidos — visual layer entrypoint.
|
|
|
|
|
*
|
|
|
|
|
* Eidos is CSS that reads `data-*` attrs emitted by the morfo-runtime
|
|
|
|
|
* (via soma) and the visual channel (via sema) and applies styling.
|
|
|
|
|
* It does not act, it does not own state, it does not decide events.
|
|
|
|
|
*
|
|
|
|
|
* Layout (translated from the legacy `air/` layer with the architectural
|
|
|
|
|
* adjustments below — air targeted terra and was class-gated; eidos
|
|
|
|
|
* targets soma+morfo and applies universally to the data-attrs the
|
|
|
|
|
* morfo declares):
|
|
|
|
|
*
|
|
|
|
|
* generated/ foundation CSS generated from EidosConfig
|
|
|
|
|
* themes/ font assets used by the base generated theme
|
|
|
|
|
* components/ recipes per component (selectors and state rules)
|
eidos: phase 1 — translate air's foundation (contracts, tokens, themes/base)
Translates the foundation of air's visual system into eidos:
- eidos/contracts/ public token interfaces (4 root + 64 components)
- eidos/tokens/ per-component token values (62 components + motion)
- eidos/themes/base/ light + dark + _static + motion (4 files)
Mechanical translation: `--air-*` → `--eidos-*` everywhere, relative
imports preserved. The CSS is currently NOT wired into the legacy air/
folder, which stays for reference until phase 4 cleanup.
The `--eidos-*` prefix is documented in eidos/index.css with the two
reasons it earns its keep:
1. Authorship clarity at debug time (DevTools can identify the layer)
2. Override discipline (consumers know they're targeting public API)
Phase 1 only adds the foundation without touching component recipes.
The hand-rolled recipes (toast, dialog, toggle, switch, collapsible)
and the legacy hand-rolled tokens.css continue to load alongside the
new tree. Phase 2 will replace recipes with air-translated equivalents
and reconcile the duplicate motion/tint tokens.
Architectural note: air targeted terra and gated its CSS with
`.air-{component}` class on a wrapper. Eidos targets soma+morfo and
applies universally to the data-attrs the morfo declares — no class
gates, no Svelte wrappers. The morfo layer is the one that decides
which `data-*` attrs exist, which is the substantive shift between
the two systems.
Verification: smoke 68/68, morfo-check 66/66, focus rings + dialog
entrance/exit still working per Playwright check (curve unchanged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
* archetypes transversal selectors keyed by `data-archetype`
|
|
|
|
|
* events reactions to `data-event-*` signals (sema visual channel)
|
|
|
|
|
*
|
|
|
|
|
* --- The `--*` token prefix ---
|
eidos: phase 1 — translate air's foundation (contracts, tokens, themes/base)
Translates the foundation of air's visual system into eidos:
- eidos/contracts/ public token interfaces (4 root + 64 components)
- eidos/tokens/ per-component token values (62 components + motion)
- eidos/themes/base/ light + dark + _static + motion (4 files)
Mechanical translation: `--air-*` → `--eidos-*` everywhere, relative
imports preserved. The CSS is currently NOT wired into the legacy air/
folder, which stays for reference until phase 4 cleanup.
The `--eidos-*` prefix is documented in eidos/index.css with the two
reasons it earns its keep:
1. Authorship clarity at debug time (DevTools can identify the layer)
2. Override discipline (consumers know they're targeting public API)
Phase 1 only adds the foundation without touching component recipes.
The hand-rolled recipes (toast, dialog, toggle, switch, collapsible)
and the legacy hand-rolled tokens.css continue to load alongside the
new tree. Phase 2 will replace recipes with air-translated equivalents
and reconcile the duplicate motion/tint tokens.
Architectural note: air targeted terra and gated its CSS with
`.air-{component}` class on a wrapper. Eidos targets soma+morfo and
applies universally to the data-attrs the morfo declares — no class
gates, no Svelte wrappers. The morfo layer is the one that decides
which `data-*` attrs exist, which is the substantive shift between
the two systems.
Verification: smoke 68/68, morfo-check 66/66, focus rings + dialog
entrance/exit still working per Playwright check (curve unchanged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
*
|
|
|
|
|
* In an app with a single owned design system there's no strict need for a
|
|
|
|
|
* prefix on custom properties: nothing else collides. We keep `--*`
|
eidos: phase 1 — translate air's foundation (contracts, tokens, themes/base)
Translates the foundation of air's visual system into eidos:
- eidos/contracts/ public token interfaces (4 root + 64 components)
- eidos/tokens/ per-component token values (62 components + motion)
- eidos/themes/base/ light + dark + _static + motion (4 files)
Mechanical translation: `--air-*` → `--eidos-*` everywhere, relative
imports preserved. The CSS is currently NOT wired into the legacy air/
folder, which stays for reference until phase 4 cleanup.
The `--eidos-*` prefix is documented in eidos/index.css with the two
reasons it earns its keep:
1. Authorship clarity at debug time (DevTools can identify the layer)
2. Override discipline (consumers know they're targeting public API)
Phase 1 only adds the foundation without touching component recipes.
The hand-rolled recipes (toast, dialog, toggle, switch, collapsible)
and the legacy hand-rolled tokens.css continue to load alongside the
new tree. Phase 2 will replace recipes with air-translated equivalents
and reconcile the duplicate motion/tint tokens.
Architectural note: air targeted terra and gated its CSS with
`.air-{component}` class on a wrapper. Eidos targets soma+morfo and
applies universally to the data-attrs the morfo declares — no class
gates, no Svelte wrappers. The morfo layer is the one that decides
which `data-*` attrs exist, which is the substantive shift between
the two systems.
Verification: smoke 68/68, morfo-check 66/66, focus rings + dialog
entrance/exit still working per Playwright check (curve unchanged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
* deliberately, for two reasons:
|
|
|
|
|
*
|
|
|
|
|
* 1. **Authorship clarity at debug time.** Inspecting an element in
|
|
|
|
|
* DevTools and seeing `--toggle-bg` immediately answers "this
|
eidos: phase 1 — translate air's foundation (contracts, tokens, themes/base)
Translates the foundation of air's visual system into eidos:
- eidos/contracts/ public token interfaces (4 root + 64 components)
- eidos/tokens/ per-component token values (62 components + motion)
- eidos/themes/base/ light + dark + _static + motion (4 files)
Mechanical translation: `--air-*` → `--eidos-*` everywhere, relative
imports preserved. The CSS is currently NOT wired into the legacy air/
folder, which stays for reference until phase 4 cleanup.
The `--eidos-*` prefix is documented in eidos/index.css with the two
reasons it earns its keep:
1. Authorship clarity at debug time (DevTools can identify the layer)
2. Override discipline (consumers know they're targeting public API)
Phase 1 only adds the foundation without touching component recipes.
The hand-rolled recipes (toast, dialog, toggle, switch, collapsible)
and the legacy hand-rolled tokens.css continue to load alongside the
new tree. Phase 2 will replace recipes with air-translated equivalents
and reconcile the duplicate motion/tint tokens.
Architectural note: air targeted terra and gated its CSS with
`.air-{component}` class on a wrapper. Eidos targets soma+morfo and
applies universally to the data-attrs the morfo declares — no class
gates, no Svelte wrappers. The morfo layer is the one that decides
which `data-*` attrs exist, which is the substantive shift between
the two systems.
Verification: smoke 68/68, morfo-check 66/66, focus rings + dialog
entrance/exit still working per Playwright check (curve unchanged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
* came from the visual layer of UIX". A bare `--toggle-bg` could
|
|
|
|
|
* come from anywhere — a third-party widget, a route-scoped style,
|
|
|
|
|
* an experiment.
|
|
|
|
|
*
|
|
|
|
|
* 2. **Override discipline.** A consumer overriding `--color-
|
eidos: phase 1 — translate air's foundation (contracts, tokens, themes/base)
Translates the foundation of air's visual system into eidos:
- eidos/contracts/ public token interfaces (4 root + 64 components)
- eidos/tokens/ per-component token values (62 components + motion)
- eidos/themes/base/ light + dark + _static + motion (4 files)
Mechanical translation: `--air-*` → `--eidos-*` everywhere, relative
imports preserved. The CSS is currently NOT wired into the legacy air/
folder, which stays for reference until phase 4 cleanup.
The `--eidos-*` prefix is documented in eidos/index.css with the two
reasons it earns its keep:
1. Authorship clarity at debug time (DevTools can identify the layer)
2. Override discipline (consumers know they're targeting public API)
Phase 1 only adds the foundation without touching component recipes.
The hand-rolled recipes (toast, dialog, toggle, switch, collapsible)
and the legacy hand-rolled tokens.css continue to load alongside the
new tree. Phase 2 will replace recipes with air-translated equivalents
and reconcile the duplicate motion/tint tokens.
Architectural note: air targeted terra and gated its CSS with
`.air-{component}` class on a wrapper. Eidos targets soma+morfo and
applies universally to the data-attrs the morfo declares — no class
gates, no Svelte wrappers. The morfo layer is the one that decides
which `data-*` attrs exist, which is the substantive shift between
the two systems.
Verification: smoke 68/68, morfo-check 66/66, focus rings + dialog
entrance/exit still working per Playwright check (curve unchanged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
* primary-element` knows they are overriding the visual layer
|
|
|
|
|
* contract, not just naming-colliding with some local variable.
|
|
|
|
|
* The prefix makes the public API surface explicit.
|
|
|
|
|
*
|
|
|
|
|
* Tokens are owned by EidosConfig and emitted by the generated foundation.
|
|
|
|
|
* Recipes inside eidos consume them via `var(--*)`. Higher layers (sema,
|
|
|
|
|
* soma, morfo) do NOT consume these tokens — those layers carry their own
|
eidos: phase 1 — translate air's foundation (contracts, tokens, themes/base)
Translates the foundation of air's visual system into eidos:
- eidos/contracts/ public token interfaces (4 root + 64 components)
- eidos/tokens/ per-component token values (62 components + motion)
- eidos/themes/base/ light + dark + _static + motion (4 files)
Mechanical translation: `--air-*` → `--eidos-*` everywhere, relative
imports preserved. The CSS is currently NOT wired into the legacy air/
folder, which stays for reference until phase 4 cleanup.
The `--eidos-*` prefix is documented in eidos/index.css with the two
reasons it earns its keep:
1. Authorship clarity at debug time (DevTools can identify the layer)
2. Override discipline (consumers know they're targeting public API)
Phase 1 only adds the foundation without touching component recipes.
The hand-rolled recipes (toast, dialog, toggle, switch, collapsible)
and the legacy hand-rolled tokens.css continue to load alongside the
new tree. Phase 2 will replace recipes with air-translated equivalents
and reconcile the duplicate motion/tint tokens.
Architectural note: air targeted terra and gated its CSS with
`.air-{component}` class on a wrapper. Eidos targets soma+morfo and
applies universally to the data-attrs the morfo declares — no class
gates, no Svelte wrappers. The morfo layer is the one that decides
which `data-*` attrs exist, which is the substantive shift between
the two systems.
Verification: smoke 68/68, morfo-check 66/66, focus rings + dialog
entrance/exit still working per Playwright check (curve unchanged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
* concerns (perceptual durations, behavior, contract DNA) that are
|
|
|
|
|
* independent of visual rendering.
|
eidos: V1 minimum viable — tokens + archetype rules + event reactions
First eidos code. Closes the cross-layer cycle that's been documented but
unproven: an actual CSS layer that consumes the morfo + sema contracts
and applies styles transversally without enumerating components.
src/uix/eidos/
- tokens.css — motion durations (quick/fast/base) + easings (standard /
emphasized / decelerate) + opacity scale (hover/disabled/faded) +
intent tints (neutral/affirm/fulfill/risk/threat) + focus ring tokens.
~30 lines. No theming dimension yet — when it arrives, these are the
base theme.
- archetypes.css — transversal selectors keyed by `data-archetype`.
Covers trigger, overlay, content, indicator, thumb, track, close,
action, item, option. Plus a universal `:focus-visible` ring rule that
applies to every archetype-classified element. Single attribute
selector specificity — components can override freely.
- events.css — reactions to perceptual signals during their ~13ms life.
`[data-event^="dismiss"]` fades out, `[data-event^="announce"]` pulses
with intent-tinted box-shadow, `[data-last-action]` tints exit
animation by causa.
- index.css — entrypoint that imports the above in cascade order.
Consumer: `import '$uix/eidos/index.css'`.
src/routes/test/eidos/+page.svelte (demo)
- Sets up Soma + App context inline (independent of /test/soma layout).
- Imports `$uix/eidos/index.css` and demonstrates the cross-layer story:
- Toggle + Switch buttons share trigger styles via `[data-archetype="trigger"]`
without per-component CSS.
- Collapsible expands/collapses with state-driven attrs.
- Toast intent buttons fire `data-event="announce" data-intent="risk|threat|affirm"`,
which eidos's events.css briefly tints via box-shadow during the signal window.
- Component-specific layout (positioning, switch thumb transform) lives in
the page's `<style>` block — eidos provides only the cross-component
baseline, NOT per-component recipes.
What this proves
- The runtime emits `data-archetype` correctly in browser (not just in
unit tests).
- A single CSS rule can target ALL triggers across all components
without enumeration. Add a new component with `archetype: 'trigger'`
and it inherits the styling automatically.
- Sema's `data-event*` signals live in the DOM long enough for CSS to
react (the ~13ms window measured by `scripts/probe-toast-dismiss.ts`
is observable as a brief box-shadow flash on toasts).
- The cross-layer cycle Morfo declara → Soma transcribe → Sema emite →
Dom aplica → Eidos lee runs end-to-end.
What V1 deliberately excludes
- Theming dimension (no `[data-theme]` overrides yet).
- Per-component recipes (each component will get its own CSS later).
- Keyframes / @starting-style / animation primitives.
- Sound / vibra (those are separate engines that subscribe to sema).
Verification
- /test/eidos returns 200; manual browser check shows triggers share
hover dim + focus ring across components, toasts flash intent tint on
appear.
- 68/68 smoke routes (smoke only scans /test/soma — eidos route is
outside that scope, manually verified).
- svelte-check unchanged at 155 (pure CSS additions).
6 months ago
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* ── Foundation: generated static fallback ─────────────────────────────────
|
|
|
|
|
*
|
|
|
|
|
* ActiveEidos is the canonical source for generated foundation CSS. This
|
|
|
|
|
* entrypoint imports the generated artifact for SSR/static docs where
|
|
|
|
|
* `ActiveEidos.create({ applyDom: false })` does not inject runtime styles.
|
|
|
|
|
* Historical `contracts/` and `themes/base/` CSS were retired once
|
|
|
|
|
* ActiveEidos became the source for generated foundation/theme CSS.
|
|
|
|
|
*/
|
eidos: phase 1 — translate air's foundation (contracts, tokens, themes/base)
Translates the foundation of air's visual system into eidos:
- eidos/contracts/ public token interfaces (4 root + 64 components)
- eidos/tokens/ per-component token values (62 components + motion)
- eidos/themes/base/ light + dark + _static + motion (4 files)
Mechanical translation: `--air-*` → `--eidos-*` everywhere, relative
imports preserved. The CSS is currently NOT wired into the legacy air/
folder, which stays for reference until phase 4 cleanup.
The `--eidos-*` prefix is documented in eidos/index.css with the two
reasons it earns its keep:
1. Authorship clarity at debug time (DevTools can identify the layer)
2. Override discipline (consumers know they're targeting public API)
Phase 1 only adds the foundation without touching component recipes.
The hand-rolled recipes (toast, dialog, toggle, switch, collapsible)
and the legacy hand-rolled tokens.css continue to load alongside the
new tree. Phase 2 will replace recipes with air-translated equivalents
and reconcile the duplicate motion/tint tokens.
Architectural note: air targeted terra and gated its CSS with
`.air-{component}` class on a wrapper. Eidos targets soma+morfo and
applies universally to the data-attrs the morfo declares — no class
gates, no Svelte wrappers. The morfo layer is the one that decides
which `data-*` attrs exist, which is the substantive shift between
the two systems.
Verification: smoke 68/68, morfo-check 66/66, focus rings + dialog
entrance/exit still working per Playwright check (curve unchanged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
/* ── Theme assets: font faces used by the base static theme tokens ───────── */
|
|
|
|
|
@import './themes/fonts.css';
|
|
|
|
|
|
|
|
|
|
/* ── Generated foundation: ActiveEidos base config static export ────────── */
|
|
|
|
|
@import './generated/base.css';
|
|
|
|
|
|
eidos: phase 1 — translate air's foundation (contracts, tokens, themes/base)
Translates the foundation of air's visual system into eidos:
- eidos/contracts/ public token interfaces (4 root + 64 components)
- eidos/tokens/ per-component token values (62 components + motion)
- eidos/themes/base/ light + dark + _static + motion (4 files)
Mechanical translation: `--air-*` → `--eidos-*` everywhere, relative
imports preserved. The CSS is currently NOT wired into the legacy air/
folder, which stays for reference until phase 4 cleanup.
The `--eidos-*` prefix is documented in eidos/index.css with the two
reasons it earns its keep:
1. Authorship clarity at debug time (DevTools can identify the layer)
2. Override discipline (consumers know they're targeting public API)
Phase 1 only adds the foundation without touching component recipes.
The hand-rolled recipes (toast, dialog, toggle, switch, collapsible)
and the legacy hand-rolled tokens.css continue to load alongside the
new tree. Phase 2 will replace recipes with air-translated equivalents
and reconcile the duplicate motion/tint tokens.
Architectural note: air targeted terra and gated its CSS with
`.air-{component}` class on a wrapper. Eidos targets soma+morfo and
applies universally to the data-attrs the morfo declares — no class
gates, no Svelte wrappers. The morfo layer is the one that decides
which `data-*` attrs exist, which is the substantive shift between
the two systems.
Verification: smoke 68/68, morfo-check 66/66, focus rings + dialog
entrance/exit still working per Playwright check (curve unchanged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
/* ── Eidos-specific layers (no equivalent in air) ───────────────────────── */
|
eidos: V1 minimum viable — tokens + archetype rules + event reactions
First eidos code. Closes the cross-layer cycle that's been documented but
unproven: an actual CSS layer that consumes the morfo + sema contracts
and applies styles transversally without enumerating components.
src/uix/eidos/
- tokens.css — motion durations (quick/fast/base) + easings (standard /
emphasized / decelerate) + opacity scale (hover/disabled/faded) +
intent tints (neutral/affirm/fulfill/risk/threat) + focus ring tokens.
~30 lines. No theming dimension yet — when it arrives, these are the
base theme.
- archetypes.css — transversal selectors keyed by `data-archetype`.
Covers trigger, overlay, content, indicator, thumb, track, close,
action, item, option. Plus a universal `:focus-visible` ring rule that
applies to every archetype-classified element. Single attribute
selector specificity — components can override freely.
- events.css — reactions to perceptual signals during their ~13ms life.
`[data-event^="dismiss"]` fades out, `[data-event^="announce"]` pulses
with intent-tinted box-shadow, `[data-last-action]` tints exit
animation by causa.
- index.css — entrypoint that imports the above in cascade order.
Consumer: `import '$uix/eidos/index.css'`.
src/routes/test/eidos/+page.svelte (demo)
- Sets up Soma + App context inline (independent of /test/soma layout).
- Imports `$uix/eidos/index.css` and demonstrates the cross-layer story:
- Toggle + Switch buttons share trigger styles via `[data-archetype="trigger"]`
without per-component CSS.
- Collapsible expands/collapses with state-driven attrs.
- Toast intent buttons fire `data-event="announce" data-intent="risk|threat|affirm"`,
which eidos's events.css briefly tints via box-shadow during the signal window.
- Component-specific layout (positioning, switch thumb transform) lives in
the page's `<style>` block — eidos provides only the cross-component
baseline, NOT per-component recipes.
What this proves
- The runtime emits `data-archetype` correctly in browser (not just in
unit tests).
- A single CSS rule can target ALL triggers across all components
without enumeration. Add a new component with `archetype: 'trigger'`
and it inherits the styling automatically.
- Sema's `data-event*` signals live in the DOM long enough for CSS to
react (the ~13ms window measured by `scripts/probe-toast-dismiss.ts`
is observable as a brief box-shadow flash on toasts).
- The cross-layer cycle Morfo declara → Soma transcribe → Sema emite →
Dom aplica → Eidos lee runs end-to-end.
What V1 deliberately excludes
- Theming dimension (no `[data-theme]` overrides yet).
- Per-component recipes (each component will get its own CSS later).
- Keyframes / @starting-style / animation primitives.
- Sound / vibra (those are separate engines that subscribe to sema).
Verification
- /test/eidos returns 200; manual browser check shows triggers share
hover dim + focus ring across components, toasts flash intent tint on
appear.
- 68/68 smoke routes (smoke only scans /test/soma — eidos route is
outside that scope, manually verified).
- svelte-check unchanged at 155 (pure CSS additions).
6 months ago
|
|
|
@import './archetypes.css';
|
|
|
|
|
@import './events.css';
|
eidos: phase 1 — translate air's foundation (contracts, tokens, themes/base)
Translates the foundation of air's visual system into eidos:
- eidos/contracts/ public token interfaces (4 root + 64 components)
- eidos/tokens/ per-component token values (62 components + motion)
- eidos/themes/base/ light + dark + _static + motion (4 files)
Mechanical translation: `--air-*` → `--eidos-*` everywhere, relative
imports preserved. The CSS is currently NOT wired into the legacy air/
folder, which stays for reference until phase 4 cleanup.
The `--eidos-*` prefix is documented in eidos/index.css with the two
reasons it earns its keep:
1. Authorship clarity at debug time (DevTools can identify the layer)
2. Override discipline (consumers know they're targeting public API)
Phase 1 only adds the foundation without touching component recipes.
The hand-rolled recipes (toast, dialog, toggle, switch, collapsible)
and the legacy hand-rolled tokens.css continue to load alongside the
new tree. Phase 2 will replace recipes with air-translated equivalents
and reconcile the duplicate motion/tint tokens.
Architectural note: air targeted terra and gated its CSS with
`.air-{component}` class on a wrapper. Eidos targets soma+morfo and
applies universally to the data-attrs the morfo declares — no class
gates, no Svelte wrappers. The morfo layer is the one that decides
which `data-*` attrs exist, which is the substantive shift between
the two systems.
Verification: smoke 68/68, morfo-check 66/66, focus rings + dialog
entrance/exit still working per Playwright check (curve unchanged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
|
|
|
|
/* ── Component recipes ──────────────────────────────────────────────────── */
|
feat(eidos): port 8 layout primitives from air (box, flex, grid, stack, group, wrap, container, section)
Visual-only primitives translated from the legacy `air/components/layout`
on the morfo-runtime branch. Architectural adjustments for the eidos
target: drop the `air-` class prefix, target `[data-{name}]` directly,
rename CSS vars to `--{name}-*`, replace `var(--air-space-N)` with the
eidos foundation `var(--space-N)` token, resolve responsive props via
`ActiveEidos.resolve(prop)`.
- `box` — universal box-model utility (size, padding, margin, gap,
position, overflow, flex/grid item props). Every prop maps to a
CSS custom property; unset props fall through via `revert-layer`.
- `flex` / `grid` / `stack` / `group` / `wrap` — layout containers
that render through Box (single `<div>` per primitive, no
semantic-tag prop in this batch). Markers stack as
`[data-box][data-{name}]`.
- `container` — max-width centered (sm/md/lg/xl/full).
- `section` — semantic-padding block with background tone variants.
Shared helpers at `_layout/shared.ts` (formatLayoutSpace /
formatLayoutLength / formatLayoutRaw / pushStyleVar / composeStyle)
plus an optional `Layout.*` namespace barrel for consumers who prefer
that style; the individual `import { Box }` from each component dir
also works.
Each primitive ships a demo at `/uix/components/{name}/` with a live
example, a props table, and a reference comparison row (radix-themes /
chakra-ui / mantine).
svelte-check: 0 errors / 0 new warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
/* Layout primitives load first so component recipes can layer on top of them. */
|
|
|
|
|
@import './components/box/box.css';
|
|
|
|
|
@import './components/flex/flex.css';
|
|
|
|
|
@import './components/grid/grid.css';
|
|
|
|
|
@import './components/stack/stack.css';
|
|
|
|
|
@import './components/group/group.css';
|
|
|
|
|
@import './components/wrap/wrap.css';
|
|
|
|
|
@import './components/container/container.css';
|
|
|
|
|
@import './components/section/section.css';
|
feat(layout): Layout Batch 2 — aspect-ratio, auto-grid, banner, float
4 new layout primitives, full canon (morfo + eidos full set + canon
demo + README). `npm run component:audit`: 81 / 81 PASS, 0 NEEDS-WORK.
**AspectRatio** — composes through Box; emits single `--aspect-ratio`
CSS var (uses modern CSS `aspect-ratio` property, drops legacy
padding-bottom hack). Accepts numeric (16/9 → 1.777…) or string
("16/9") values.
**AutoGrid** — composes through Grid. Resolves `templateColumns` from
`minItemWidth` to `repeat(auto-fill, minmax(MIN, 1fr))` in JS so the
recipe stays declarative. Uses `auto-fill` (not auto-fit) to preserve
empty tracks when item count is low.
**Banner** — `<header role="banner">` announcement strip with intent
(full 8-role UIX `ColorRole`), variant (`ChipVariant` soft/solid/
outline/ghost), size (sm/md/lg). Dismissal is composition-driven:
consumer wraps in {#if show} and adds `<Banner.Close>`. No
`dismissible` boolean.
**Float** — CSS `float` primitive with logical `inline-start` /
`inline-end` sides. Redefined from air's 9-zone overlay primitive
(which becomes a future `<Positioned>` component if real demand
surfaces). Useful for inline images / pull-quotes / drop caps.
Each component ships morfo (`scope: ['eidos']`, 1 part, 0 events,
justified) + eidos set + README (Baseline / Comparativa /
Decisiones / Eventos Sema / Gaps with disposition markers /
Referencias / Passive justification) + canon demo with 6 tabs.
Sidebar nav appends the 4 entries to the existing Layout group.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/aspect-ratio/aspect-ratio.css';
|
|
|
|
|
@import './components/auto-grid/auto-grid.css';
|
|
|
|
|
@import './components/banner/banner.css';
|
feat(eidos): Button + 9 demo rebuilds + picker recipe polish
Build:
- New <Button> compound (morfo + soma + sema + eidos): 6 variants
(solid/soft/surface/outline/ghost/plain), 8 colors, 5 sizes, optional
icon/endIcon snippets, loading state with built-in Spinner, asChild
Slot pattern, intent-driven sema (commit-action with fromProp:intent).
Demo rebuilds (canonical depth per DEMO_AUTHORING_GUIDE):
- context-menu, dropdown-menu, pin-input, alert-dialog, toggle-group:
full per-part Morfo contracts, reference-parity tables, ARIA contract
tables, keyboard tables, composition controls.
- color-field, time-field, time-picker, time-range-picker, color-picker,
month-grid, year-grid: same canonical treatment; per-part data/aria/
keyboard iteration of raw morfo, reference parity vs react-aria/
ark-ui/Chakra/bits-ui/Mantine.
Picker recipe polish:
- PickerShell.Clear/Cancel/Close refactored to use <Button size="xs">
(affects 5 pickers — date / date-range / time / time-range / color).
- TimePicker + TimeRangePicker: popover condensed (min-width 18rem -> 14
/ 32 -> 28), per-channel accent (hour=primary, minute=secondary,
second=neutral; end variant muted), ruler off by default, hour ruler
cleaned to 0/6/12/18/23.
- ColorPicker: typography token --_color-picker-trigger-font-size that
scales with data-size (10/12/14px); autoFormat default true; Area
recipe override for [data-x-channel='hue'] with rainbow gradient +
saturation/brightness Y overlays.
- ValueText eidos wrapper: fix children snippet bug that suppressed
soma's displayValue fallback.
- date-field.css: segment editing affordance (cursor + hover + active
ring) via new --_date-field-accent-element token across 8 color
cascades.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/button/button.css';
|
feat(layout): Layout Batch 2 — aspect-ratio, auto-grid, banner, float
4 new layout primitives, full canon (morfo + eidos full set + canon
demo + README). `npm run component:audit`: 81 / 81 PASS, 0 NEEDS-WORK.
**AspectRatio** — composes through Box; emits single `--aspect-ratio`
CSS var (uses modern CSS `aspect-ratio` property, drops legacy
padding-bottom hack). Accepts numeric (16/9 → 1.777…) or string
("16/9") values.
**AutoGrid** — composes through Grid. Resolves `templateColumns` from
`minItemWidth` to `repeat(auto-fill, minmax(MIN, 1fr))` in JS so the
recipe stays declarative. Uses `auto-fill` (not auto-fit) to preserve
empty tracks when item count is low.
**Banner** — `<header role="banner">` announcement strip with intent
(full 8-role UIX `ColorRole`), variant (`ChipVariant` soft/solid/
outline/ghost), size (sm/md/lg). Dismissal is composition-driven:
consumer wraps in {#if show} and adds `<Banner.Close>`. No
`dismissible` boolean.
**Float** — CSS `float` primitive with logical `inline-start` /
`inline-end` sides. Redefined from air's 9-zone overlay primitive
(which becomes a future `<Positioned>` component if real demand
surfaces). Useful for inline images / pull-quotes / drop caps.
Each component ships morfo (`scope: ['eidos']`, 1 part, 0 events,
justified) + eidos set + README (Baseline / Comparativa /
Decisiones / Eventos Sema / Gaps with disposition markers /
Referencias / Passive justification) + canon demo with 6 tabs.
Sidebar nav appends the 4 entries to the existing Layout group.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/float/float.css';
|
feat(layout): Layout Batch 3 — scroll-area, separator, splitter + splitter sema rewire
3 new layout primitives close the air port set (15 / 15 done).
`npm run component:audit`: 91 / 91 PASS, all 3 new PASS with 0 errors.
**scroll-area** — eidos compound wrapper over the existing soma:
`<ScrollArea><ScrollArea.Viewport>{content}</ScrollArea.Viewport>
<ScrollArea.Scrollbar orientation="vertical"><ScrollArea.Thumb />
</ScrollArea.Scrollbar><ScrollArea.Corner /></ScrollArea>`. Mirrors
Radix Primitives / Bits UI shape. Demo 667 lines.
**separator** — visual primitive (`scope: ['eidos']`) with conditional
ARIA: `role="separator"` + `aria-orientation` when not decorative,
`aria-hidden="true"` when decorative. New morfo
`src/uix/morfo/components/separator.ts`. Demo 645 lines.
**splitter** — eidos compound over existing soma:
`<Splitter orientation="horizontal"><Splitter.Panel>{a}</Splitter.Panel>
<Splitter.ResizeTrigger /><Splitter.Panel>{b}</Splitter.Panel>
</Splitter>`. Demo 824 lines.
**Splitter sema rewire — match Slider's handle/drag/commit shape:**
The morfo previously declared only `commit-resize`. Direct-manipulation
primitives (Slider, Splitter) should share the same event vocabulary so
audio/haptic projections feel coherent across the family. Renamed +
expanded to mirror Slider:
- `handle-pick` (family: handle, verb: pick, sequence: coincident,
target: resize-trigger) — fires on pointerdown when user grabs the
trigger.
- `handle-drag` (family: handle, verb: drag, sequence: coincident,
target: resize-trigger) — fires on each pointermove frame.
- `commit-set` (family: commit, verb: set, intent: neutral,
sequence: post, target: provider) — fires on pointerup. The runtime
`commit-set` handler is wired to call the consumer's `onResizeEnd`
callback, so sema and JS callback land at the same moment (parity
with Slider's `onValueCommit`).
Soma provider triggers these via `this.runtime.trigger(name)` in
`onpointerdown` / `onpointermove` / `notifyResizeEnd` (the latter
unchanged externally — internally now routes through the runtime
trigger).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/scroll-area/scroll-area.css';
|
|
|
|
|
@import './components/separator/separator.css';
|
|
|
|
|
@import './components/splitter/splitter.css';
|
feat(typography): Text — POC consuming new --style-* foundation tokens
First typography primitive consuming the named-style tokens added in
`015bd761`. Sets the canon for the remaining typography ports
(Heading, Display, Code, CodeBlock, Kbd, Mark, Highlight, Link).
API:
- `style: 'body' | 'prose' | 'label' | 'caption'` (default `body`).
Selects which `--style-{name}-*` token set the recipe consumes.
- Per-axis overrides via `size`, `family`, `weight`, `color`, `align`.
Each writes a local `--_text-{axis}` CSS variable that wins over
the named style's default via the cascade.
- `italic`, `underline` shorthand booleans.
- `truncate` (single-line ellipsis) + `clamp={N}` (multi-line via
CSS `-webkit-line-clamp`).
- `as` prop switches the tag (default `'span'` — inline composable).
- `tagStyle` escape hatch for one-off inline CSS that the prop
surface doesn't cover (separate from the `style` named-style prop).
Architectural choices documented in README:
- Sizes map to eidos `xxs..xxxl` (not air's `1..12`).
- Families use the eidos slot names (`primary/secondary/mono/display`)
not air's domain aliases (`ui/prose/code`) — coherence with the
foundation contract.
- Colors are restricted to the content-role slots
(`--color-content-*`) to preserve contrast guarantees. Free colors
via `tagStyle`.
- Canvas line-count measurement (air's `text` + bindable `lineCount`)
deferred; CSS line-clamp covers the visual-truncate use case.
Sidebar nav gains a `Typography` group with Text as its first entry.
`npm run component:audit`: 82 / 82 PASS, 0 NEEDS-WORK. svelte-check 0
errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/text/text.css';
|
feat(typography): 8 primitives — Heading, Display, Code, CodeBlock, Kbd, Mark, Highlight, Link
Full canon for the rest of the typography family, following Text
(commit `03531709`) as template. Each primitive ships morfo (passive,
scope eidos, 0 events) + full eidos set (svelte/css/types/index +
README with required sections) + canon 6-tab demo (≥400 lines each).
**Heading** — semantic h1..h6 via `level` prop, visual style decoupled
via `style` prop (defaults to match level but `<Heading level={3}
style="h1">` allowed). Demo 594 lines.
**Display** — always-h1 page-title primitive. Always uses
`style="hero"`. Tracking via literal em values until foundation emits
hero-scale tracking tokens. Demo 546 lines.
**Code** — inline code via `<code>`. `variant: 'plain' | 'soft' |
'outline'` for subtle background chrome. Demo 423 lines.
**CodeBlock** — multi-line `<pre><code>` with optional title +
copy button (button utility, NOT a sema verb — kept out of morfo per
2-of-3 rule). Demo 402 lines.
**Kbd** — keyboard shortcut chrome over `style="code"`. Demo 418 lines.
**Mark** — text highlight via `<mark>`. Palette uses canonical
`ColorRole` intents (default `fulfill` ≈ legacy yellow). Demo 402 lines.
**Highlight** — substring highlighter inside Text. Scans children for
`query` and wraps matches. Same intent palette as Mark. Demo 407 lines.
**Link** — text link via `<a>`. `variant: 'default' | 'subtle' |
'plain'` for underline style, `intent: ColorRole` for color. Passive
on the morfo — hover/active is CSS-only, navigation belongs to the
consumer (router). Demo 448 lines.
**Architectural deviations from air documented per README's
Decisiones section:**
- Heading: semantic level vs visual style decoupling
- Code split into inline `<Code>` + block `<CodeBlock>` (was one
component with `variant='block'` in air)
- Mark/Highlight palette swapped from literal hue names to canonical
ColorRole intents (theme contract)
- Link palette opened to full ColorRole; `quiet`/`emphasized` air
variants dropped in favour of composable patterns
`npm run component:audit`: **90 / 90 PASS**, 0 NEEDS-WORK.
svelte-check: 0 errors, 0 new warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/heading/heading.css';
|
|
|
|
|
@import './components/display/display.css';
|
|
|
|
|
@import './components/code/code.css';
|
|
|
|
|
@import './components/code-block/code-block.css';
|
|
|
|
|
@import './components/kbd/kbd.css';
|
|
|
|
|
@import './components/mark/mark.css';
|
|
|
|
|
@import './components/highlight/highlight.css';
|
|
|
|
|
@import './components/link/link.css';
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/icon/icon.css';
|
|
|
|
|
@import './components/avatar/avatar.css';
|
|
|
|
|
@import './components/breadcrumb/breadcrumb.css';
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/toast/toast.css';
|
|
|
|
|
@import './components/dialog/dialog.css';
|
feat(alert-dialog): port from soma to eidos (Tier 1 sprint, 2/5)
Modal Dialog variant with role=alertdialog, forced modal=true, and
click-outside disabled. Soma delegates to the Dialog runtime
(variant='alertdialog') so DOM markers are Dialog's (data-dialog-*)
— eidos reuses Dialog's recipe for chrome and adds two
alert-dialog-specific buttons: Action + Cancel.
Eidos shape:
- Provider, Content, Action, Cancel — alert-dialog-specific wrappers
- Trigger, Overlay, Title, Description, Header, Footer, Portal —
re-exported from Dialog's eidos barrel (zero duplication)
Recipe (alert-dialog.css):
- Only styles Action + Cancel
- Buttons consume Toggle palette tokens (same "interactive button"
vocabulary used by <Toggle> and <ToggleGroup.Item>)
- Auto-color cascade: Action picks up the dialog's data-color so a
risk/threat-flagged dialog gets a red/amber Action button without
manual recipe code
- Cancel stays neutral so the safe-choice contrast is preserved
Morfo:
- scope: ['soma'] → ['soma', 'sema', 'eidos']
- The morfo declares Action + Cancel parts only (Provider is virtual,
other parts inherited via Dialog runtime delegation)
- Classified passive at this contract level (Dialog owns the events);
README documents this in §"Passive justification"
Demo: 6-tab canonical template with intent / size / position chips,
soma + eidos snippets, references comparison table vs Radix
Primitives / Bits UI / Ark UI / Chakra v3 / Radix Themes / shadcn.
Checks: svelte-check 0 errors, component:audit 91/91 PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/alert-dialog/alert-dialog.css';
|
feat(dropdown-menu): port from soma to eidos (Tier 1 sprint, 3/5)
Full 15-part compound — pass-through wrappers over soma with the
canonical visual recipe.
Parts: Provider · Trigger · Content · Arrow · Item · Group ·
GroupHeading · Separator · CheckboxItem · CheckboxGroup · RadioGroup
· RadioItem · Sub · SubTrigger · SubContent.
API parity with Bits UI (richest reference): CheckboxGroup +
RadioGroup binding arrays / scalars, nested Sub-menus, Arrow pointer.
Radix Primitives + Ark UI lack CheckboxGroup; Chakra v3 + Radix
Themes lack Arrow.
Recipe (dropdown-menu.css):
- Uniform chrome across Content + SubContent (panel shadow,
border, padding, max-height)
- Items share one set of tokens (hover/focus highlight, disabled
opacity, indicator slot via `::before` for Checkbox/Radio
data-state='checked')
- SubTrigger gets a chevron via `::after`
- GroupHeading: muted uppercase, micro-tracked
- Separator: 1px subtle divider
- Enter animation on `data-state='open'`
No `variant` / `color` / `size` knobs at the eidos layer — menus are
themselves the visual variant of "list of actions" within an app.
Per-item destructive coloring deferred to next pass (cascade via
item's `data-color` attr).
Morfo scope: ['soma'] → ['soma', 'sema', 'eidos']. Sound comes from
the family bases (`emerge` for open/close, `commit` for select) — no
per-component cascade needed.
Demo: 6-tab template, items hooked to `lastSelected` tracker, all
three groupings demoed (Items / CheckboxGroup / RadioGroup / Sub).
Checks: svelte-check 0 errors, component:audit 91/91 PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/dropdown-menu/dropdown-menu.css';
|
|
|
|
|
@import './components/context-menu/context-menu.css';
|
feat(eidos): port Menubar (desktop File/Edit/View bar)
#129. Eidos wrapper + types + recipe + canonical-depth demo. Soma owns
active-menu state, cross-menu ArrowLeft/Right navigation, hover-switch
when active, focus return on Escape, typeahead, RTL. Eidos paints the
bar surface and trigger states; per-menu Content reuses the
dropdown-menu recipe (Item / Sub / Checkbox / Radio chrome + the shared
menu-indicator partial from `a2709476`) because soma already
re-exports the DropdownMenu parts for those slots.
Reference parity:
| Feature | Radix | bits-ui | ark-ui | this port |
|---|---|---|---|---|
| Root provider w/ active-menu binding | ✓ | ✓ | ✗ (Menu only) | ✓ |
| Menu / Trigger / Content | ✓ | ✓ | ✓ | ✓ |
| Item / Group / GroupHeading / Separator | ✓ | ✓ | ✓ | ✓ |
| CheckboxItem / CheckboxGroup / RadioGroup / RadioItem | ✓ | ✓ | ✓ | ✓ |
| Sub / SubTrigger / SubContent | ✓ | ✓ | ✓ | ✓ |
| Cross-menu ArrowLeft/Right (with loop) | ✓ | ✓ | n/a | ✓ |
| Auto-open on hover when active | ✓ | ✓ | n/a | ✓ |
| Typeahead | ✓ | ✓ | ✓ | ✓ |
| Arrow part | ✓ | ✓ | ✓ | ⚠ dropped — menubar idiom anchors to bar edge |
Parts shipped (15): Provider, Menu, Trigger, Portal, Content, Item,
Group, GroupHeading, Separator, Arrow (re-exported), CheckboxItem,
CheckboxGroup, RadioGroup, RadioItem, Sub, SubTrigger, SubContent. The
inner 12 (Item onwards) are re-exports of the eidos DropdownMenu
wrappers so the menu-indicator partial applies automatically — no
recipe duplication.
Variant set: only `size: 'sm' | 'md' | 'lg'` (xs would crowd triggers,
xl unnecessary for a chrome bar). Size cascades from Menubar root to
per-menu Content via a Svelte context (`menubar-context.ts`,
`getMenubarSizeContext` / `setMenubarSizeContext`).
Cross-layer: eidos Menubar reuses eidos DropdownMenu internals — the
recipe doesn't redefine Item/Sub/Checkbox/Radio. Menubar.Content stamps
`data-size` AND inherits `data-dropdown-menu-content` (soma's
MenubarContent specializes DropdownMenuContent), so dropdown-menu.css
paints the panel chrome and our menubar.css only declares the bar
surface + trigger states.
Workaround note: menubar-content.svelte casts rest props through
`Record<string, unknown>` to bypass a TS "union too complex" depth
limit triggered by soma's inline-augmented MenuContentProps + HTML
attribute union. Runtime types are correct.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/menubar/menubar.css';
|
feat(eidos): port Command (⌘K palette)
Closes #130. Eidos wrapper + 13 part wrappers + 2 new sub-parts +
Dialog composition + recipe + canonical-depth demo. Soma owns scoring
(`computeFuzzyScore`), keyboard nav (Arrow + vim Ctrl+N/P/J/K +
typeahead + loop + grid columns + RTL), item visibility, and the
imperative `CommandApi`. Eidos adds size + Item.Icon / Item.Shortcut
slots + Dialog composition with optional shortcut binding.
Reference parity:
| Feature | cmdk | Mantine Spotlight | bits-ui Command | this port |
|---|---|---|---|---|
| Root provider w/ bind:value, bind:search | ✓ | ✓ | ✓ | ✓ |
| Input, List, Viewport, Empty | ✓ | ✓ | ✓ | ✓ |
| Group + GroupHeading + GroupItems | ✓ | ✓ | ✓ | ✓ |
| Item value, keywords, disabled, onSelect | ✓ | ✓ | ✓ | ✓ |
| LinkItem (anchor) | ✗ | ✗ | ✓ | ✓ |
| Separator, Loading (progress 0-100) | ✓ | partial | ✓ | ✓ |
| Custom filter / shouldFilter | ✓ | ✓ | ✓ | ✓ |
| Loop, vim bindings, grid columns | ✓ | partial | ✓ | ✓ |
| disablePointerSelection | ✓ | ✗ | ✓ | ✓ |
| Modal Dialog composition | ✓ | ✓ (Root is modal) | ✓ | ✓ via eidos Dialog |
| Built-in ⌘K shortcut binding | ✗ (BYO) | ✓ | ✗ (BYO) | ✓ optional |
| Item.Icon / Item.Shortcut slots | BYO | leftSection/rightSection | BYO | ✓ NEW |
Naming: `Command.Root` aliased as the default export (cmdk/bits-ui
parity, eidos convention). Inner parts as named sub-properties:
`Command.Input`, `Command.List`, `Command.Item`, `Command.Item.Icon`,
`Command.Item.Shortcut`, `Command.Dialog`, …
Variant set: only `size: 'sm' | 'md' | 'lg'` (drops xs/xl — palette UX
needs comfortable hit targets, matches air baseline).
Shortcut binding: cross-platform `mod` token (Cmd on macOS, Ctrl
elsewhere). Parses `'mod+k'`, `'ctrl+shift+p'`, etc. Window keydown
listener registered on mount, cleaned up on unmount. Pass `null`/
undefined to disable.
Recipe: `command.css` paints palette container, input row, scrolling
List/Viewport, item rows (with highlighted [data-selected] state),
icon + shortcut slot layout, group heading, separator, empty state.
Loading progress is a 2px bar under Input with `--_command-loading-
progress` for the fill. Inside Command.Dialog the panel chrome comes
from dialog.css.
Demo: 6 tabs (Live/API/Morfo/Sema/Recipe/A11y) — Live wires every
soma + eidos prop + Dialog mode with shortcut input. Two stages:
inline palette + dialog opener with shortcut hint. Three code
snippets (soma, eidos inline, eidos dialog).
Gotcha note: command.svelte renames the children prop to `bodyContent`
on destructure to avoid shadowing the inner `{#snippet children}` — same
infinite-recursion trap noted in `feedback_button_consumer_pattern.md`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/command/command.css';
|
feat(eidos): port Listbox (embedded selection list)
#131. Eidos wrapper + types + recipe + canonical-depth demo. Soma owns
single/multi selection (value as string[]), roving-tabindex keyboard
nav (Arrow + Home/End + typeahead + Ctrl+A), Field-merged flags
(disabled/readonly/required/invalid), hidden form inputs. Eidos adds
the visual surface and exposes variant + size + color.
Reference parity (no air baseline — port directly from soma):
| Feature | soma | react-aria | Headless UI | bits-ui | this port |
|---|---|---|---|---|---|
| Single + multiple selection | ✓ | ✓ | ✓ | ✓ | ✓ |
| bind:value (controlled) | ✓ | ✓ | ✓ | ✓ | ✓ |
| Typeahead + Arrow / Home / End | ✓ | ✓ | ✓ | ✓ | ✓ |
| Loop nav | ✓ | ✗ | ✗ | ✗ | ✓ |
| Section / Group + label | ✓ | ✓ | ✗ | ✓ | ✓ |
| Ctrl+A select-all (multi) | ✓ | ✓ | ✗ | ✗ | ✓ |
| Form participation (hidden inputs) | ✓ | ✗ | ✓ | ✗ | ✓ |
| Field.Provider merge (disabled/invalid/…) | ✓ | ✗ | ✗ | ✗ | ✓ |
| Horizontal orientation | ✓ | ✓ | ✓ | ✓ | ✓ |
| Item indicator (✓ glyph) | ✓ | ✓ | ✗ | ✓ | ✓ shared partial |
| Drag-to-reorder | ✗ | ✓ | ✗ | ✗ | ⚠ deferred |
| Async / infinite loading | ✗ | ✓ | ✗ | ✗ | ⚠ deferred |
| selectionBehavior (toggle/replace) | ⚠ implicit | ✓ | ✗ | ✓ | ⚠ deferred |
| PageUp/Down nav | ✗ | ✓ | ✗ | ✗ | ⚠ deferred |
Parts (5): Listbox (Root, replaces soma's Provider name), Item,
ItemIndicator, Group, GroupLabel.
Variant set:
- variant: ControlVariant (`'surface' | 'outline' | 'ghost'`), default 'surface'
- size: xs..xl (5-step canonical form-control scale)
- color: ColorRole (primary/secondary/neutral/affirm/fulfill/risk/threat/loss)
- block: stretch to container width
Cross-component reuse:
- `ItemIndicator` shares the menu-indicator visual (`a2709476`) — ✓ glyph
identical to standalone Checkbox and Dropdown/Context Menu
CheckboxItems. CSS-only difference: shown only inside
`[data-listbox-item][data-selected]`.
- Item row hover / highlight tokens mirror Combobox.Item /
Command.Item — the form-control surface family stays coherent.
Disambiguation note (in index.ts comment):
- Listbox: atomic options, text rows, no inner interactive elements
(ARIA forbids interactive descendants inside role="listbox")
- GridList: rows with buttons / links / checkboxes inside (2D nav)
- Select / Combobox: closed picker surface (Listbox-like dropdown)
Gotcha: listbox.svelte and listbox-item.svelte rename `children` to
`bodyContent` on destructure to avoid shadowing the inner
`{#snippet children}` block (same recursion trap documented in
`feedback_button_consumer_pattern.md` Rule 3).
Demo: 6 tabs at canonical depth — Live wires all soma + eidos props,
all 3 ControlVariant chips, 5 size chips, 8 ColorRole chips, single +
multi mode toggle, RTL/horizontal toggle. Sidebar entry added under
Forms (between Combobox and Calendar).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/listbox/listbox.css';
|
feat(eidos): port NavigationMenu (top-level site navigation w/ mega-menus)
#132. Eidos wrapper + types + recipe + canonical-depth demo. 7 parts:
Provider, List, Item, Trigger, Content, Link, Indicator. Soma owns
active-item state, hover delays (openDelay/skipDelayDuration/closeDelay),
hoverEnabled gate, loop arrow nav, data-motion directional swap,
Indicator rect CSS vars. Eidos paints the bar + trigger chrome +
Content panel + slide animations.
Reference parity (no air baseline — port directly from soma):
| Feature | soma | Radix | bits-ui | this port |
|---|---|---|---|---|
| Provider + List + Item + Trigger + Content + Link + Indicator | ✓ | ✓ | ✓ | ✓ |
| Hover open (openDelay/skipDelayDuration/closeDelay) | ✓ | ✓ | ✓ | ✓ |
| hoverEnabled (click-only mode) | ✓ | ⚠ | ✓ | ✓ |
| orientation horizontal + vertical | ✓ | ✓ | ⚠ | ✓ |
| dir / RTL | ✓ | ✓ | ✓ | ✓ |
| Indicator CSS vars (x/y/w/h rect) | ✓ | ✗ | ✗ | ✓ |
| data-motion directional swap (from-/to-start/end) | ✓ | ✓ | ⚠ | ✓ |
| forceMount on Content + Indicator | ✓ | ✓ | ✓ | ✓ |
| ARIA <nav> + translated aria-label | ✓ | ⚠ | ⚠ | ✓ |
| Keyboard nav (Arrow/Home/End/Enter/Space/Esc, ArrowDown → Content) | ✓ | ✓ | ✓ | ✓ |
| Sub (nested viewports) | ✗ | ✓ | ✓ | ⚠ deferred (matches soma) |
| Viewport (shared swap pane) | ✗ | ✓ | ✓ | ⚠ deferred |
| Modal variant (focus trap) | ✗ | ✓ | ✗ | ⚠ deferred |
| SafePolygon hover bridge | ✗ | ✗ | ✗ | ⚠ deferred |
Naming: matches soma's part names exactly. Both Trigger (button) and
Link (anchor) stay native — NOT routed through <Button> — because
top-nav has flat/borderless chrome distinct from action buttons. The
Link automatically picks up `aria-current="page"` styling for the
active route.
Variant set: only `size: 'sm' | 'md' | 'lg'` (no xs/xl). No
variant/color — NavigationMenu is chrome surface, not actionable
intent.
Fresh recipe — does NOT reuse dropdown-menu (floating click-driven)
or menubar (roving tabindex bar). NavigationMenu lives inline in the
page; per-item Content panels render via Floating but conceptually
attach to the bar. Indicator is a 2px underline that slides via CSS
vars (--navigation-menu-indicator-{x,y,w,h}) written by soma. Trigger
chevron rotates on [data-state='open']. Content animates via
data-motion directional slide on sibling swap.
Sidebar entry added under Navigation (between Pagination and a future
NavigationMenu entry).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/navigation-menu/navigation-menu.css';
|
feat(eidos): port GridList (selectable list w/ interactive row children)
Closes #133. Eidos wrapper + types + recipe + canonical-depth demo.
4 parts: Provider, Row, Cell, SelectionCheckbox. Soma owns selection
(none/single/multiple), 2D keyboard nav (Up/Down rows + Left/Right
cells), Shift/Ctrl range/toggle, Ctrl+A select-all, Escape-clear,
typeahead, hidden form input, Field merge. Eidos adds variant + size +
color + defaultValue (uncontrolled init).
Reference parity (no air baseline):
| Feature | soma | react-aria GridList | this port |
|---|---|---|---|
| selectionMode none/single/multiple | ✓ | ✓ | ✓ |
| bind:value (controlled) | ✓ | ✓ | ✓ |
| defaultValue (uncontrolled init) | ✗ | ✓ | ✓ added (parity with Combobox/Select) |
| Item with interactive descendants | ✓ | ✓ | ✓ |
| 2D keyboard nav (Up/Down rows, L/R cells) | ✓ | ✓ | ✓ |
| Shift/Ctrl range/toggle | ✓ | ✓ | ✓ |
| Ctrl+A select-all (multiple) | ✓ | ✓ | ✓ |
| Escape-clear | ✓ | ✓ | ✓ |
| Typeahead | ✓ | ✓ | ✓ |
| ARIA grid/row/gridcell | ✓ | ✓ | ✓ |
| Field merge (disabled/invalid/required/readonly) | ✓ | ✗ | ✓ |
| Form participation (hidden input) | ✓ | ✗ | ✓ |
| Drag-to-reorder | ✗ | ✓ | ⚠ deferred (compose w/ future DragDrop) |
| onAction (row click → navigate) | ✗ | ✓ | ⚠ deferred (use button/link inside Cell) |
| selectionBehavior toggle/replace | ✗ implicit | ✓ | ⚠ deferred |
| disabledBehavior 'selection'/'all' | ✗ skip-only | ✓ | ⚠ deferred |
| Async / infinite loading | ✗ | ✓ | ⚠ deferred |
| Empty state slot | ✗ | ✓ | ⚠ document `data-empty` styling |
Naming: keeps soma's part names (Provider, Row, Cell, SelectionCheckbox).
react-aria's "GridListItem" maps to `Row`; soma's vocabulary is more
precise and consistent with future Table / TreeGrid in the same family.
Variant set:
- variant: ControlVariant (surface/outline/ghost), default 'surface'
- size: xs..xl (5-step, mirrors Combobox/Listbox)
- color: ColorRole (primary/secondary/neutral/affirm/fulfill/risk/threat/loss)
- block: stretch to container width
Disambiguation (in index.ts comment): use GridList for rows with
interactive children; use Listbox for atomic text options (ARIA
forbids interactive descendants inside role="listbox"); use
Select/Combobox for closed-picker patterns; use Table for tabular
data with columns + sort.
Visual reuse:
- SelectionCheckbox uses the same `mask-image: url(svg-check)` pattern
as the shared menu-indicator partial (commit a2709476). Filled
accent box → white ✓ on `[data-state='checked']`.
- Row hover/highlight tokens mirror Combobox.Item / Listbox.Item so
the form-control surface family stays coherent.
- Same `max-block-size: min(60vh, 22rem) + overflow-y: auto` scroll
contract as Listbox (commit 830025b4).
Sidebar entry under Forms (between Listbox and Calendar).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/grid-list/grid-list.css';
|
|
|
|
|
@import './components/time-range-field/time-range-field.css';
|
feat(eidos): port carousel from soma
Eidos wrapper for the soma Carousel primitive — horizontally or
vertically scrollable rail of slides with keyboard, button, indicator,
drag and autoplay nav. Soma owns the index + autoplay timer + gesture;
eidos adds size/variant/color cascades and renders prev/next triggers
as `<Button iconOnly variant="ghost" rounded="full">` per the canonical
Button consumer pattern.
## Feature comparison
| Feature | embla | Mantine | ark-ui | Eidos |
|----------------------|-------|---------|--------|-------|
| Index nav | ✓ | ✓ | ✓ | ✓ |
| Loop | ✓ | ✓ | ✓ | ✓ |
| Drag / scrub | ✓ | — | ✓ | ✓ |
| Autoplay | plugin| ✓ | ✓ | ✓ |
| Pause on hover | plugin| ✓ | ✓ | ✓ |
| Orientation V/H | ✓ | ✓ | ✓ | ✓ |
| Keyboard nav | ✓ | ✓ | ✓ | ✓ |
| Indicators | — | ✓ | ✓ | ✓ |
| Prev/Next triggers | — | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 5 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
| Multi-slides view | ✓ | ✓ | — | deferred |
| Lazy-mount slides | plugin| — | — | deferred |
## Parts (8)
Provider · Viewport · ItemGroup · Item · PrevTrigger · NextTrigger ·
IndicatorGroup · Indicator.
PrevTrigger and NextTrigger render Button via the soma `child` snippet
(canonical Button consumer pattern). Default chrome:
`iconOnly variant='ghost' rounded='full'` + chevron glyph. Size cascades
from Carousel root; consumers can override every Button visual prop
inline. Passing `child` bypasses Button entirely.
## Recipe
Card chrome around the rail (`variant='surface'`), absolute-positioned
triggers over the viewport edges (vertical flips to top/bottom). Active
indicator stretches inline-size 2.25× for clearer current-position hint.
8 ColorRole accents.
## Demo
Canonical 6-tab depth (~470L): Live / API / Morfo / Sema / Recipe /
A11y. Live controls cover all soma props (orientation, loop, dragEnabled,
autoplay + delay slider, pauseOnHover, disabled) and all eidos props
(variant, size, color). 4 colored placeholder slides for visual contrast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/carousel/carousel.css';
|
|
|
|
|
/* Shared menu-item indicator partial — must load AFTER the menu recipes
|
|
|
|
|
so its `padding-inline-start` calc wins over the row rule's
|
|
|
|
|
`padding-inline` shorthand at equal specificity. */
|
|
|
|
|
@import './lib/menu-indicator.css';
|
feat(pin-input): port from soma to eidos (Tier 1 sprint, 5/5 — DONE)
Multi-cell OTP / verification code input. Two parts (Provider, Cell)
with a snippet-driven cell iteration.
Architecture:
- A transparent absolutely-positioned <input> captures every
keystroke, paste and IME event (soma).
- Visible bordered cells follow the hidden input's caret via
data-active / data-filled emitted by the morfo.
- Single-input approach (shadcn / Bits family) — preserves paste
distribution, autocomplete='one-time-code' for SMS autofill on
iOS/Android, and IME quality.
Eidos surface:
- size: 'sm' | 'md' | 'lg' | 'xl' (responsive), cascades cell
dimension + glyph size tokens to all cells
- Recipe paints square bordered cells with focus ring tracking
data-active, subtle bg lift on data-filled, threat-toned border
when [data-invalid] cascades from the provider
Morfo:
- scope: ['soma'] → ['soma', 'sema', 'eidos']
- apg URL added (Textbox pattern — OTP has no formal ARIA pattern)
- Passive at the morfo layer (no events array; perceptual feedback
via the commit family base when onComplete fires)
- README documents the passive classification
Comparativa: aligned with shadcn / Bits UI (single hidden input).
Ark and Chakra use one input per cell which loses paste / IME
quality.
Sidebar nav: 'Pin input' added under Forms (next to Tags input).
=== Sprint Tier 1 — DONE ===
1. toggle-group 61ffbade
2. alert-dialog d5630a52
3. dropdown-menu 4e477e63
4. context-menu 21eb8a62
5. pin-input (this)
Checks: svelte-check 0 errors, component:audit 91/91 PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/pin-input/pin-input.css';
|
|
|
|
|
@import './components/date-range-field/date-range-field.css';
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/drawer/drawer.css';
|
|
|
|
|
@import './components/field/field.css';
|
|
|
|
|
@import './components/form/form.css';
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/popover/popover.css';
|
|
|
|
|
@import './components/tooltip/tooltip.css';
|
feat(eidos): port LinkPreview (Radix HoverCard equivalent)
#128. Eidos wrapper + types + recipe + canonical-depth demo. Trigger
renders as `<a>` (navigational anchor); on hover/focus after `openDelay`
(default 700ms) the Content panel reveals with SafePolygon bridging
trigger and content so the pointer can travel without dismissing.
Non-modal, no-op on touch (link follows directly).
Reference parity:
| Feature | Radix HoverCard | bits-ui LinkPreview | ark-ui HoverCard | this port |
|---|---|---|---|---|
| open with bind: | ✓ | ✓ | ✓ | ✓ |
| openDelay (700ms) / closeDelay (300ms) | ✓ | ✓ | ✓ | ✓ |
| disabled | ✗ | ✓ | ✓ | ✓ |
| Trigger as `<a>` | ✓ | ✓ | ✗ (`<button>`) | ✓ |
| Portal + Arrow | ✓ | ✓ | ✓ | ✓ |
| Floating layer (side/align/offsets/collision) | ✓ | ✓ | ✓ | ✓ |
| customAnchor | ✗ | ✓ | ✗ | ✓ |
| ContentStatic | ✗ | ✓ | ✗ | ⚠ deferred |
| lazyMount | ✗ | ✗ | ✓ | ⚠ deferred |
Naming choice: `LinkPreview` (matches air + bits-ui), not `HoverCard`.
Trigger semantic is navigational anchor — for a button-triggered
hover-card use `<Popover>` with `openDelay` or pass `child` snippet
to bypass the default `<a>`.
Variant set: only `size: 'xs'..'xl'` (no ControlVariant — LinkPreview
is a surface, not actionable). Defer color/intent per the canon rule
that surfaces are uncolored.
Parts shipped: Provider, Trigger, Portal, Content, Arrow.
Recipe: `link-preview.css` with anchor underline (subtle, intensifies
on hover/open), content panel chrome (border + surface bg + shadow),
arrow fill matching panel bg, slide+fade entrance per `data-side`,
reduced-motion skip.
Demo: 6 tabs (Live/API/Morfo/Sema/Recipe/A11y) at canonical depth per
DEMO_AUTHORING_GUIDE — Live wires every Provider+Content prop with
the right chip set, eidos+soma snippets generated, all 6 ARIA
contract rows + keyboard + pointer tables in A11y tab.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/link-preview/link-preview.css';
|
|
|
|
|
@import './components/progress/progress.css';
|
|
|
|
|
@import './components/meter/meter.css';
|
|
|
|
|
@import './components/number-field/number-field.css';
|
|
|
|
|
@import './components/pagination/pagination.css';
|
|
|
|
|
@import './components/toggle/toggle.css';
|
|
|
|
|
@import './components/toggle-group/toggle-group.css';
|
docs + switch: align active docs to canon, migrate switch to eidos wrapper
Phase 4 (docs alignment with the canonical guide):
- active_architecture.md — Dialog example refactored to the new event
shape (target inside semantic, explicit verb + sequence). Verbs
table grouped by family with the post-canon vocabulary (select/
toggle moved to commit, edit removed from handle, new verbs added).
Section 10 (Estado actual) updated to 2026-05-08 with current
progress: doctrina cerrada, eidos en producción, SoundChannel
eager-init, switch migrated, etc. "Para profundizar" §14 points at
the canonical guide.
- morfo/README.md — Step 5.5 added covering the doctrinal event
shape with all fields (semantic.family, .verb, .target, .sequence,
.intent) and the two naming shapes ({verb}-{variant} and
{family}-{verb}).
- sema/README.md — verbs section restructured to Record-by-family
matching the runtime; documents the verbs that moved between
families per the canon. Naming shapes section updated with both
forms and validateEventName signature.
- CLAUDE.md — "Session hand-off" rewritten with the post-canon
state. Pointer at GUIA_IMPLEMENTACION_SEMAUIX.md as the canonical
source. Pending list refreshed: switch is the immediate next
component, persistence/a11ySemantic/polymorphism deferred until
real consumers appear.
Switch migration to the eidos wrapper pattern:
- morfo/components/switch.ts — adds events: [{ commit-toggle }] with
the doctrinal shape (verb=toggle, sequence=post, intent fromProp
binding to the 4 supported intents). New data-* (data-color with
the 6-token Toggle/Switch subset, data-readonly, data-invalid,
data-required) and corresponding aria-*. scope expanded to
['soma', 'sema'].
- soma/components/switch/types.ts — adds intent, color, readonly,
invalid, aria-labelledby props with full JSDoc. Subset constrained
per guide §3.1 (Toggle/Switch row).
- soma/components/switch/switch-provider.svelte.ts — runtime-direct,
Field integration via FieldProvider.get(), isDisabled/isReadonly/
isRequired/isInvalid OR-merge derivations, intent ↔ color
resolution (intent wins when evaluative), runtime.trigger(
'commit-toggle') in onclick.
- soma/components/switch/components/switch.svelte — wires the new
props through bindProps.
- eidos/components/switch/{switch.css, switch.svelte, types.ts,
index.ts, README.md} — full subdirectory pattern, mirroring
toggle. Recipe drops legacy success/warning/danger/info selectors;
declares the 6 doctrinal data-color values directly. Wrapper
auto-renders Switch.Thumb inside the track and exposes an optional
`thumb` snippet for in-thumb glyphs.
- eidos/components/switch.css (legacy flat) deleted; eidos/index.css
updated to import the subdirectory recipe.
- web/routes/switch/+page.svelte — interactive testbed with all soma
props as live controls + tabs (Live, Soma, Morfo, Sema, Eidos,
Notes). Mirrors toggle's structure.
npm run check holds at the 39-error baseline. Tests 217/217 green.
Pending:
- Migrate collapsible / dialog / drawer / popover / toast / avatar
to the eidos wrapper pattern.
- Persistence + holds-by-intent — defer until first signal.warn /
signal.alert consumer.
- a11ySemantic + polymorphic events — defer until concrete need.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/switch/switch.css';
|
collapsible: migrate to eidos wrapper pattern (third pilot)
Third component migrated to the eidos wrapper subdirectory pattern,
following the new "check air baseline first" discipline.
Air baseline recovered from commit 0a391408^:
- air/components/collapsible: 3 wrappers (Provider/Trigger/Content)
with `class` passthrough, `bind:open`, `onOpenChange`, manual
`air.interaction.play('expansion', enter|exit)` perceptual emit.
- No size, intent, color, or chevron slot.
Migration decisions (presented BEFORE coding per scope-approval rule):
- Two doctrinal events (`expand` + `collapse`, both emerge family,
sequence='pre', no intent — emerge is transitional). Replaces air's
manual `air.interaction.play` with `runtime.trigger('expand'|
'collapse')` which atravieses visual + sound + vibra channels.
- No intent/color/size — air didn't have them and disclosure is
non-evaluative per guide §3.1.
- Multi-part API: Provider + Trigger + Content (no flat default per
doctrine §10).
Files:
- src/uix/morfo/components/collapsible.ts: scope expanded to ['soma',
'sema'], events array added with two emerge events.
- src/uix/soma/components/collapsible/collapsible-provider.svelte.ts:
runtime.events handlers `expand`/`collapse` mutate state; toggle()
fires runtime.trigger(next ? 'expand' : 'collapse').
- src/uix/eidos/components/collapsible/{collapsible.css,
collapsible-provider.svelte, collapsible-trigger.svelte,
collapsible-content.svelte, types.ts, index.ts, README.md}.
- src/uix/eidos/components/collapsible.css (legacy flat) deleted.
- src/uix/eidos/index.css: import path updated to subdirectory.
- web/routes/collapsible/+page.svelte: full interactive testbed with
tabs (Live, Soma, Morfo, Sema, Eidos, Notes) mirroring switch's
structure. Sema tab shows per-channel signature cards for both
expand and collapse events.
Disciplina formalizada en CLAUDE.md hand-off + memory entry: "Check
air baseline first" before any future eidos migration. Switch shipped
without this check and lost ResponsiveProp<Size> (regression). Don't
repeat with collapsible/dialog/drawer/popover/toast/avatar.
npm run check holds at the 39-error baseline. Tests 218/218 green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/collapsible/collapsible.css';
|
uix: drawer/popover/toast/accordion eidos wrappers + sema packs + /uix docs scaffold
- eidos: migrate drawer / popover / toast / accordion to wrapper subdirectory
pattern (provider + parts + tokens + recipe per component); avatar wrapper
added; toggle/switch/collapsible recipe polish.
- sema: per-component packs (accordion, drawer, popover, toast); engine /
resolver / event / sema-map updates; visual channel hold refinements.
- morfo: drawer event surface (variant, dragToDismiss, isDragging, minSize/
maxSize, onDragStart); popover/toast/accordion event refinements; schema +
types updates.
- soma: drawer provider rewrite (variant, dragToDismiss, isDragging binding,
drag callbacks); toast provider/toaster restructure with toast-main +
toast-status parts; popover content/arrow polish; gesture/floating layer
updates; manipulation layer scaffold; avatar removed (eidos owns it now).
- web/routes: legacy demos updated for new APIs; new /uix docs scaffold
(web/routes/uix/) with Radix-style shell, theme/lang/format/semantic prefs,
and standardized component template (Drawer page first — 6 tabs, live
controls, drag-hint arrows, scroll-safe body wrapper).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/accordion/accordion.css';
|
|
|
|
|
@import './components/tabs/tabs.css';
|
|
|
|
|
@import './components/checkbox/checkbox.css';
|
|
|
|
|
@import './components/radio-group/radio-group.css';
|
|
|
|
|
@import './components/rating-group/rating-group.css';
|
|
|
|
|
@import './components/search-field/search-field.css';
|
|
|
|
|
@import './components/select/select.css';
|
|
|
|
|
@import './components/combobox/combobox.css';
|
|
|
|
|
@import './components/calendar/calendar.css';
|
feat(eidos): port range-calendar from soma + extract shared partial
Standalone eidos `<RangeCalendar>` wrapper over the soma range-calendar
primitive. Adds size / variant / color cascades; otherwise pass-through
14 compound parts (Provider, Header, Heading, PrevButton, NextButton,
MonthSelect, YearSelect, Grid, GridHead, GridBody, GridRow, HeadCell,
Cell, Day).
## Feature comparison
| Feature | bits-ui | ark-ui | react-aria | Eidos |
|----------------------|---------|--------|------------|-------|
| Range start/end | ✓ | ✓ | ✓ | ✓ |
| Hover preview state | ✓ | ✓ | ✓ | ✓ |
| minDays / maxDays | — | — | ✓ | ✓ |
| allowSingleDay | ✓ | ✓ | ✓ | ✓ |
| preventDeselect | ✓ | ✓ | — | ✓ |
| Multi-month display | ✓ | ✓ | ✓ | ✓ |
| Week-number column | — | — | ✓ | ✓ |
| Month/Year selects | — | ✓ | — | ✓ |
| Disabled / readonly | ✓ | ✓ | ✓ | ✓ |
| isDateUnavailable | ✓ | ✓ | ✓ | ✓ |
| Holiday markers | — | — | — | ✓ |
| Locale / dir | ✓ | ✓ | ✓ | ✓ |
| Size cascade | — | — | — | ✓ 4 |
| Variant cascade | — | — | — | ✓ 3 |
| Color cascade | — | — | — | ✓ 8 |
## Range-cells partial extracted
The recipe at `src/uix/eidos/components/range-calendar/range-calendar.css`
now owns ALL `[data-range-calendar-*]` selectors — the shared rules
that previously lived inside `date-range-picker.css` (lines 537-834)
moved here. Both consumers (the standalone `<RangeCalendar>` and
`<DateRangePicker>`'s popover content) stamp the same data-attrs, so the
same selectors paint without duplication.
`date-range-picker.css` drops 298 lines of duplication; it keeps only
the picker-specific scope overrides (`[data-date-range-picker-calendar]`
with no chrome, max-content sizing, transparent bg).
## Morfo
Extended `rangeCalendarMorfo.scope` to include `'eidos'` so the new
wrapper appears in the eidos-layer inventory and the demo Morfo tab
declares it correctly.
## Demo
Canonical 6-tab depth (~600L) at `/uix/components/range-calendar`. Live
tab exposes every range-specific prop: minDays / maxDays sliders,
allowSingleDay, preventDeselect, numberOfMonths chips (1 / 2 / 3),
pagedNavigation, fixedWeeks, showWeekNumbers, disableDaysOutsideMonth,
weekStartsOn, disabled, readonly, locale, plus size / variant / color
cascades.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/range-calendar/range-calendar.css';
|
feat(month-grid, year-grid): standalone primitives parallel to date-picker views
DatePicker.MonthView / YearView require a DatePickerProvider context and
can't be used standalone. This commit introduces MonthGrid and YearGrid
as independent primitives that accept value / placeholder / minValue /
maxValue as props and work anywhere.
morfo
- New morfo/components/month-grid.ts and year-grid.ts.
- scope: ['soma','sema'], apg: grid pattern.
- 7 parts each: Provider (role=application) / Header / Heading /
PrevButton / NextButton / Grid (role=grid) / Cell (role=gridcell).
- Events: commit-set (cell click) + nav-step (prev/next).
- Cell data-attrs: data-value, data-selected, data-current, data-disabled,
data-focused.
- Keyboard map covers arrows / Home / End / PageUp/Down / Enter / Space.
soma
- New soma/components/{month-grid,year-grid}/ with provider class +
langs + types + exports + 7 thin svelte wrappers each.
- MonthGridProvider: 12-cell grid keyed to placeholder.year; localized
month names via Intl.DateTimeFormat with configurable monthFormat
('long'|'short'|'narrow'). Prev/Next step ±1 year.
- YearGridProvider: decadeSize-cell grid (default 12); page anchored at
placeholder.year - (placeholder.year % decadeSize). Prev/Next step ±
decadeSize.
- Both expose Cell sub-provider with snippetProps {selected, disabled,
current, value, label} + roving tabindex + RTL-aware keyboard nav.
eidos
- New eidos/components/{month-grid,year-grid}/ — namespace-style index
with size / variant / color visual props + dedicated CSS reusing the
--calendar-* token family.
- Each grid renders Header (Prev/Heading/Next) + 3-col Grid.
demo
- New /uix/components/month-grid and /uix/components/year-grid demo
pages with the standard live/api/morfo/sema/recipe/a11y tabs,
interactive controls for every public prop, soma+eidos snippets that
reactively rebuild from controls, and sema event play buttons.
Verified: svelte-check --threshold error -> 0 errors / 0 warnings /
4572 files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/month-grid/month-grid.css';
|
|
|
|
|
@import './components/year-grid/year-grid.css';
|
|
|
|
|
@import './components/date-field/date-field.css';
|
uix: date-picker + date-range-picker components + component audit infra
Two new Eidos components shipped end-to-end (wrapper + recipe + demo +
Soma provider hardening) plus a checklist-driven audit pipeline that
scores all 67 morfo components against doctrinal completion criteria.
New components:
- date-picker: full popover-anchored picker over date-field + calendar,
with calendar/content/trigger parts and demo route.
- date-range-picker: standalone wrapper with own calendar/grid/segment
surface, demo route, and recipe CSS.
- Both wrappers follow Option C disciplined (root + parts attached via
explicit assignment, no Object.assign).
Supporting Soma changes:
- range-calendar provider tightened (211 LOC of behavior, 167 LOC of
tests), README brought up to component doctrine.
- date-field, date-picker, date-range-field, date-range-picker Soma
providers + READMEs updated for new wrappers.
- popover provider/close gain props needed by the picker wrappers.
Morfo updates:
- date-picker / date-range-picker / range-calendar morfos refined for
the new APIs (parts, events, ARIA).
Audit infrastructure (new):
- src/uix/COMPONENT_COMPLETION_CHECKLIST.md — 81 doctrinal rules across
morfo / eidos wrapper / recipe CSS / demo / README / cross-layer
scripts. Each rule keyed to active_architecture.md, GUIA_IMPLEMENTACION
and DEMO_AUTHORING_GUIDE.
- scripts/component-audit.ts + `npm run component:audit` — regex parser
over all 67 components, emits tmp/component-audit.md with summary
scoreboard + per-component findings. Validates against canonical
SEMA_FAMILIES / SEMA_VERBS / ARCHETYPE_VOCABULARY / INTENTS.
- Initial baseline: 1 PASS, 64 NEEDS-WORK, 2 BROKEN (tooltip,
date-range-picker). Top systemic gaps: translations.label (49),
README Gaps/Comparativa/Baseline sections (87 combined), keyboard
/event ratio under-declaration (15), apg URL absent (19).
Misc:
- src/uix/kimi-audit-eidos.md — supplementary audit notes.
- .gitignore: ignore .codex-* agent scratch artifacts at repo root.
- continue.md + READMEs updated through the migration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/date-picker/date-picker.css';
|
|
|
|
|
@import './components/date-range-picker/date-range-picker.css';
|
|
|
|
|
@import './components/time-field/time-field.css';
|
|
|
|
|
@import './components/color-field/color-field.css';
|
|
|
|
|
@import './components/picker-shell/picker-shell.css';
|
feat(eidos): add TimePicker wrapper + demo following P-1..P-5 picker contract
Builds on the existing soma TimePicker slider-based clock (HourSlider /
MinuteSlider / SecondSlider + DayPeriodToggle). Adds the canonical
picker shell:
- mode='inline' | 'modal' on the Provider, propagated to popover.modal
- Provider helpers commit() / cancel() / clear() with watch(open)
snapshot for cancel-revert
- Footer composition (TimePicker.Footer / Clear / Cancel / Close as
pure-composition Eidos parts; no *Button root props)
- New TimePicker.Clock soma component that carries the
data-time-picker-clock contract attr, wraps the slider stack
- Eidos shell: Provider visual root, Trigger (clock icon), Content
(popover content), Clock, Footer + action parts, slider/toggle
pass-throughs
Renews the time-picker morfo to declare Provider / Trigger / Clock /
DayPeriodToggle / DayPeriodItem / Footer / Clear / Cancel / Close,
plus all 6 commit/emerge events (open + close-commit/cancel/dismiss/
dismiss-outside + commit-clear). apg=dialog-modal.
Demo at /uix/components/time-picker exercises all controls, Footer
composition switches, soma + eidos code snippets, morfo / sema / recipe
/ a11y tabs. README documents the picker contract.
67/67 component-audit PASS, 39/39 morfo:check PASS, svelte-check
0 errors / 0 warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/time-picker/time-picker.css';
|
|
|
|
|
@import './components/time-range-picker/time-range-picker.css';
|
feat(eidos): add ColorPicker wrapper + demo
Composite popover: 2D saturation/brightness area, hue + alpha channel
sliders, preset swatches, eye-dropper, and a segmented channel input
composed from ColorField. Builds on the existing soma color-picker
(23 parts) — eidos layer is mostly thin pass-throughs adding visual
chrome.
What landed
-----------
- New `src/uix/eidos/components/color-picker/`:
- Root visual (Provider) + visual-context (size/variant/color)
- Trigger with a default swatch dot tinted from
`--cp-trigger-color` (the air pattern from morfo-runtime)
- Content (popover wrapper), Area (+ Background + Thumb),
TransparencyGrid
- ChannelSlider (+ Track + Thumb) — generic per-channel slider;
default-composes Track+Thumb when no children
- SwatchGroup + SwatchTrigger + Swatch + SwatchIndicator
- EyeDropper (Pipette icon by default)
- ChannelInput + ChannelSegment + FormatSelect (composed from
ColorField parts; same provider injected automatically)
- Label, Control, ValueSwatch, ValueText, HiddenInput
- Recipe section `color-picker` in `recipes/base.ts` (regenerated
`generated/base.css`) — trigger, content, area, channel slider,
swatches, eye-dropper tokens.
- CSS reuses color-field tokens for the segmented input portion and
adds picker-specific visuals. Transparency checker on trigger swatch
+ value swatch + alpha slider track via conic-gradient pattern.
- Morfo updates: scope `['soma','sema']`, `apg: dialog-modal`, added
`commit-set` event so audit classifies it as interactive.
- Soma exports: ColorPickerProvider class so eidos Trigger can read
the current value for the swatch tint.
- Demo at `/uix/components/color-picker` with the standard 6 tabs;
live stage exercises area drag + channel sliders + preset swatches
+ eye-dropper + format switch.
- README documents the surface, comparativa vs air/Ark/Chakra, and
flags the picker contract (mode/Footer/commit/cancel) as gap to
align with date/time-picker in a follow-up.
Checks
------
- 67/67 component-audit PASS
- 40/40 morfo:check PASS (new color-picker demo validates against
morfo DOM)
- svelte-check 0 errors / 0 warnings
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
@import './components/color-picker/color-picker.css';
|
|
|
|
|
@import './components/slider/slider.css';
|
|
|
|
|
@import './components/toolbar/toolbar.css';
|
|
|
|
|
@import './components/tag-group/tag-group.css';
|
|
|
|
|
@import './components/tags-input/tags-input.css';
|
|
|
|
|
@import './components/file-upload/file-upload.css';
|
|
|
|
|
@import './components/editable/editable.css';
|
|
|
|
|
@import './components/stepper/stepper.css';
|