docs(theming): los 21 de la mañana recuperan su README y su pestaña Tokens

La revisión de hoy (`REVIEW-theming-2026-08-23.md`, punto P4) destapó que
NINGUNO de los commits de tokenización de la mañana del 23 hizo los pasos 8 y 9
del protocolo: `git show --stat` de los diecisiete sólo tocaba CSS, base.ts, el
ledger y las fichas. Veintidós componentes tokenizados sin la tabla de tokens en
su README y veintiuno sin la pestaña que la enseña viva.

QUÉ ENTRA

- **README «Talla y tema»** en 22 (los 21 con demo + `surface`, que no tiene
  ruta). La tabla NO está escrita a mano: se DERIVA de `lib/recipes/base.ts` y
  de `theming-sentinel-exceptions.ts`, así que lo que el lector ve es lo que el
  contrato dice. Hasta 30 claves va la tabla completa clave → valor por defecto;
  por encima, agrupada por parte (con la paleta en su propio grupo, que si no se
  come la tabla: button tiene 70 claves de tono de 106). Debajo, lo que el guard
  da por silencioso con su razón medida, y la excepción POR PATRÓN cuando la
  razón es la cascada de paleta.
- **Pestaña `Tokens`** en los 21 con demo, con el conteo real de claves.

VERIFICADO EN EL NAVEGADOR, LOS 21

Cada pestaña abre y lista exactamente sus claves: menubar 35 · field 68 ·
onion-menu 19 · button 106 · callout 34 · empty-state 26 · dropdown-menu 28 ·
context-menu 28 · checkbox 47 · image 23 · anchor-nav 12 · banner 53 ·
date-range-field 7 · time-range-field 7 · tooltip 23 · section 4 · sticky 1 ·
button-group 1 · qr-code 10 · toggle-group 1 · split-button 1.

⚠ Y el verificador mintió DOS veces antes de acertar: primero buscaba el rótulo
de la cabecera del panel en `innerText` (no lo encontraba aunque la tabla estaba
ahí), y después clicaba con `element.click()` tras `waitUntil: 'load'` — la
página aún NO ha hidratado, así que el `onclick` de Svelte no está enganchado y
el clic no hace nada. Con `networkidle` + el clic de Playwright (que espera a
que el nodo sea accionable), los 21 en verde. Es la ley de siempre: el
instrumento miente primero.

`audio-player` queda fuera: no tiene README propio — hueco aparte, anotado.

Verificación: `npm run check` sin errores nuevos en las 21 demos (los 72
globales son ajenos y preexistentes) · `docs:check` 0 · cada README conserva su
estructura de encabezados y **ninguno gana un hunk de prettier** (mi sección se
formateó dentro de su propio recorte, sin tocar lo ajeno).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alpha-0.1-background
dev 2 months ago
parent bfe7727b0c
commit e522e04e65

@ -32,6 +32,27 @@ floor = v1). Headless behavior:
spec-precise value for "current position within a page" (NOT `page`). Only
1 of 5 references sets `aria-current` at all.
## Talla y tema
**12 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `anchor-nav`).
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--anchor-nav-…`) | Valor por defecto |
| ------------------------- | --------------------------------- |
| `indent` | `var(--space-3)` |
| `rail-width` | `2px` |
| `rail-fg` | `var(--color-border-subtle)` |
| `active-rail-fg` | `var(--color-primary-solid)` |
| `font-size` | `var(--size-sm-font-size)` |
| `line-height` | `var(--size-sm-font-line-height)` |
| `link-padding-block` | `var(--space-1)` |
| `link-radius` | `var(--radius-sm)` |
| `link-fg` | `var(--color-content-muted)` |
| `hover-link-fg` | `var(--color-content-primary)` |
| `active-link-fg` | `var(--color-primary-text)` |
| `active-link-font-weight` | `var(--font-weight-medium)` |
## Comparativa
| Ref | Qué adoptamos | Qué no |

@ -52,6 +52,18 @@ Air's CSS prefix (`air-banner`) is dropped; selectors target
`[data-banner]` directly. CSS variables renamed from `--air-banner-*` to
`--banner-*` (public) and `--_banner-*` (internal recipe-local).
## Talla y tema
**53 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `banner`), con sus pasos por talla y el nombre RESUELTO que consume la receta.
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Grupo | Claves |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `raíz` | `gap` · `radius` · `line-height` · `border-width` |
| `escalas` | `padding-block-sm` · `padding-block-md` · `padding-block-lg` · `padding-inline-sm` · `padding-inline-md` · `padding-inline-lg` · `font-size-sm` · `font-size-md` · `font-size-lg` |
| `paleta` | `primary-solid` · `primary-contrast` · `primary-track` · `primary-border` · `primary-text` · `secondary-solid` · `secondary-contrast` · `secondary-track` · `secondary-border` · `secondary-text` · `neutral-solid` · `neutral-contrast` · `neutral-track` · `neutral-border` · `neutral-text` · `affirm-solid` · `affirm-contrast` · `affirm-track` · `affirm-border` · `affirm-text` · `fulfill-solid` · `fulfill-contrast` · `fulfill-track` · `fulfill-border` · `fulfill-text` · `risk-solid` · `risk-contrast` · `risk-track` · `risk-border` · `risk-text` · `threat-solid` · `threat-contrast` · `threat-track` · `threat-border` · `threat-text` · `loss-solid` · `loss-contrast` · `loss-track` · `loss-border` · `loss-text` |
## Comparativa
| Capacidad | UIX (eidos) | Chakra UI Alert | Mantine Alert | MUI Alert |

@ -43,6 +43,16 @@ codebase:
roving focus, `commit-toggle` event) with its own `ToggleGroup.Item`;
ButtonGroup is **passive** and takes real standalone Buttons.
## Talla y tema
**1 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `button-group`).
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--button-group-…`) | Valor por defecto |
| -------------------------- | ----------------- |
| `divider-strength` | `18%` |
## Comparativa
| Capability | Chakra v2 | MUI | Mantine | Bootstrap | Radix Themes | **UIX** |

@ -70,6 +70,47 @@ font-size/font-weight/icon-size` por talla (`-xs`…`-xl`), `font-family`,
que la cascada `--button-palette-*` resuelve según `data-color` (piloto del
patrón; THEMING §25 admite escalas donantes per-instance).
## Talla y tema
**106 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `button`), con sus pasos por talla y el nombre RESUELTO que consume la receta.
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Grupo | Claves |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `escalas` | `height-xs` · `height-sm` · `height-md` · `height-lg` · `height-xl` · `padding-inline-xs` · `padding-inline-sm` · `padding-inline-md` · `padding-inline-lg` · `padding-inline-xl` · `gap-xs` · `gap-sm` · `gap-md` · `gap-lg` · `gap-xl` · `font-size-xs` · `font-size-sm` · `font-size-md` · `font-size-lg` · `font-size-xl` · `font-weight-xs` · `font-weight-sm` · `font-weight-md` · `font-weight-lg` · `font-weight-xl` · `radius-sm` · `radius-md` · `radius-lg` · `radius-xl` · `radius-full` |
| `raíz` | `font-family` · `line-height` · `letter-spacing` · `border-width` · `shadow` · `transition-duration` · `transition-ease` |
| `icon` | `icon-size-xs` · `icon-size-sm` · `icon-size-md` · `icon-size-lg` · `icon-size-xl` |
| `estado` | `disabled-opacity` |
| `paleta` | `primary-track` · `primary-element` · `primary-border` · `primary-solid` · `primary-solid-hover` · `primary-text` · `primary-contrast` · `secondary-track` · `secondary-element` · `secondary-border` · `secondary-solid` · `secondary-solid-hover` · `secondary-text` · `secondary-contrast` · `neutral-track` · `neutral-element` · `neutral-border` · `neutral-solid` · `neutral-solid-hover` · `neutral-text` · `neutral-contrast` · `affirm-track` · `affirm-element` · `affirm-border` · `affirm-solid` · `affirm-solid-hover` · `affirm-text` · `affirm-contrast` · `fulfill-track` · `fulfill-element` · `fulfill-border` · `fulfill-solid` · `fulfill-solid-hover` · `fulfill-text` · `fulfill-contrast` · `risk-track` · `risk-element` · `risk-border` · `risk-solid` · `risk-solid-hover` · `risk-text` · `risk-contrast` · `threat-track` · `threat-element` · `threat-border` · `threat-solid` · `threat-solid-hover` · `threat-text` · `threat-contrast` · `loss-track` · `loss-element` · `loss-border` · `loss-solid` · `loss-solid-hover` · `loss-text` · `loss-contrast` · `palette-track` · `palette-element` · `palette-border` · `palette-solid` · `palette-solid-hover` · `palette-text` · `palette-contrast` |
**Lo que el guard R-5.4 da por silencioso, con su razón medida**
(`scripts/theming-sentinel-exceptions.ts`):
- `icon-size-xs` — the demo button carries no icon; a real <svg> child mounted and the size forced -> reaches (14px -> 123px)
- `icon-size-sm` — same, no icon in the demo; mounted -> reaches (16px -> 123px)
- `icon-size-md` — same, no icon in the demo; mounted -> reaches (18px -> 123px)
- `icon-size-lg` — same, no icon in the demo; mounted -> reaches (20px -> 123px)
- `icon-size-xl` — same, no icon in the demo; mounted -> reaches (32px -> 123px)
- `radius-sm` — the radius scale is an opt-in prop ([data-rounded]) and the demo does not set it; forced -> reaches (4px -> 1234px)
- `radius-lg` — same opt-in attribute; forced -> reaches (10px -> 1234px)
- `radius-xl` — same opt-in attribute; forced -> reaches (16px -> 1234px)
- `radius-full` — same opt-in attribute; forced data-rounded=full -> reaches (9999px -> 1234px)
- `transition-duration` — the guard freezes transitions to measure and this token IS the transition; measured unfrozen -> reaches (0.12s -> 11.5s, five properties)
- `transition-ease` — same instrument limit; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(7))
- `disabled-opacity` — no disabled button in the demo; forced data-disabled -> reaches (0.4 -> 0.137)
- `primary-track` — the FALLBACK of the palette forward: it acts only on an instance with NO data-color, and the demo stamps data-color=primary. Removed the attribute -> reaches. See the pattern entry below for…
- `primary-element` — same fallback, same condition
- `primary-border` — same fallback, same condition
- `primary-solid` — same fallback: without data-color it repaints (oklch(0.5556 0.1829 305.86) -> rgb(1,2,3)); with data-color=primary the generic forward rule supersedes it
- `primary-solid-hover` — same fallback, same condition
- `primary-text` — same fallback, same condition
- `primary-contrast` — same fallback, same condition
- `palette-track` — a RESOLVED name, declared on [data-button] by the forward, so a :root write loses BY DESIGN - a theme moves the tone or the scale. Measured on a soft button: from :root nothing, from the nod…
- `palette-element` — same resolved name; the theme moves its coordinate
- `palette-border` — same resolved name; the theme moves its coordinate
- **Por patrón** `/^(secondary|neutral|affirm|fulfill|risk|threat|loss)-(track|element|border|solid|solid-hover|text|contrast)$/` — la cascada de paleta: el bloque genérico `[data-button][data-color]` se emite el ÚLTIMO y gana por orden, así que la pintura sale del `--palette-*` global (§13, pendiente de firma).
## Comparativa
| Capacidad | Soma/Eidos | React Aria | Radix Themes | Chakra | Mantine |

@ -24,6 +24,25 @@ rule (parity floor = v1).
33 donor scales AND raw CSS colors resolve through the THM-2 shared layer
with zero extra CSS.
## Talla y tema
**34 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `callout`).
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Grupo | Claves |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `raíz` | `gap` · `padding` · `accent-width` · `radius` · `font-size` · `line-height` · `fg` |
| `row` | `row-gap` |
| `icon` | `icon-size` |
| `title` | `title-font-weight` |
| `paleta` | `primary-track` · `primary-text` · `primary-solid` · `secondary-track` · `secondary-text` · `secondary-solid` · `neutral-track` · `neutral-text` · `neutral-solid` · `affirm-track` · `affirm-text` · `affirm-solid` · `fulfill-track` · `fulfill-text` · `fulfill-solid` · `risk-track` · `risk-text` · `risk-solid` · `threat-track` · `threat-text` · `threat-solid` · `loss-track` · `loss-text` · `loss-solid` |
**Lo que el guard R-5.4 da por silencioso, con su razón medida**
(`scripts/theming-sentinel-exceptions.ts`):
- **Por patrón** `/^(primary|secondary|neutral|affirm|fulfill|risk|threat|loss)-(track|text|solid)$/` — la cascada de paleta: el bloque genérico `[data-callout][data-color]` se emite el ÚLTIMO y gana por orden, así que la pintura sale del `--palette-*` global (§13, pendiente de firma).
## Comparativa
| Ref | Equivalente | Qué adoptamos | Qué no |

@ -23,6 +23,40 @@ Radix, Bits UI, Reka/Ark y shadcn-svelte.
Partes publicas: `Indicator`, `HiddenInput`, `Group`, `GroupLabel`.
## Talla y tema
**47 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `checkbox`).
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Grupo | Claves |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `raíz` | `size-xs-box-size` · `size-xs-indicator-size` · `size-xs-indicator-stroke` · `size-sm-box-size` · `size-sm-indicator-size` · `size-sm-indicator-stroke` · `size-md-box-size` · `size-md-indicator-size` · `size-md-indicator-stroke` · `size-lg-box-size` · `size-lg-indicator-size` · `size-lg-indicator-stroke` · `size-xl-box-size` · `size-xl-indicator-size` · `size-xl-indicator-stroke` · `radius` · `border-width` · `transition-duration` · `transition-ease` · `stroke-duration` · `stroke-ease` · `bg-off` · `border-off` · `group-gap` · `group-label-font-family` · `group-label-font-size` · `group-label-font-weight` · `group-label-line-height` · `group-label-fg` |
| `estado` | `invalid-border` · `invalid-ring` · `invalid-ring-gap` · `hover-border-off` · `disabled-bg` · `disabled-border` · `disabled-opacity` · `disabled-indicator-fg` · `disabled-group-label-fg` |
| `indicator` | `indicator-fg` |
| `paleta` | `primary-solid` · `primary-solid-hover` · `neutral-solid` · `neutral-solid-hover` · `secondary-solid` · `secondary-solid-hover` · `affirm-solid` · `affirm-solid-hover` |
**Lo que el guard R-5.4 da por silencioso, con su razón medida**
(`scripts/theming-sentinel-exceptions.ts`):
- `size-xs-indicator-stroke` — the glyph exists only inside a CHECKED box and the first checkbox of the demo is empty; measured on a checked one with data-size forced -> reaches (2px -> 7px)
- `size-sm-indicator-stroke` — same, forced -> reaches (2px -> 7px)
- `size-md-indicator-stroke` — same, forced -> reaches (2px -> 7px)
- `size-lg-indicator-stroke` — same, forced -> reaches (2px -> 7px)
- `size-xl-indicator-stroke` — same, forced -> reaches (2px -> 7px)
- `invalid-border` — only under [data-invalid]; forced -> reaches (oklch(0.9555 0.0207 13.86) -> rgb(1,2,3))
- `invalid-ring` — the invalid focus ring: needs [data-invalid] AND :focus-visible, which the guard blurs on purpose; focused by hand -> reaches (the 4px ring turns rgb(1,2,3))
- `invalid-ring-gap` — same double ring; forced -> reaches (the 2px gap turns rgb(4,5,6))
- `disabled-bg` — only under [data-disabled]; forced -> reaches
- `disabled-border` — only under [data-disabled]; forced -> reaches
- `disabled-opacity` — only under [data-disabled]; forced -> reaches (0.4 -> 0.137)
- `disabled-indicator-fg` — needs [data-disabled] AND a checked box (the glyph); forced on a checked one -> reaches
- `disabled-group-label-fg` — the label carries its OWN [data-disabled] - it is not inherited from the group, so forcing the group does nothing; set on the label -> reaches (oklch(0.5032 0 0) -> rgb(1,2,3))
- `transition-duration` — the guard freezes transitions and this token IS the transition; measured unfrozen -> reaches (0.12s -> 11.5s)
- `transition-ease` — same instrument limit; measured unfrozen -> reaches
- `stroke-duration` — same, on the check path dash animation; measured unfrozen -> reaches (0.12s -> 11.5s)
- **Por patrón** `/^(primary|secondary|neutral|affirm)-(solid|solid-hover)$/` — la cascada de paleta: el bloque genérico `[data-checkbox][data-color]` se emite el ÚLTIMO y gana por orden, así que la pintura sale del `--palette-*` global (§13, pendiente de firma).
## Comparativa
| Capacidad | Eidos/Soma | APG | Radix | Bits UI | Reka/Ark | shadcn-svelte |

@ -68,6 +68,43 @@ Soma owns:
- Same keyboard navigation as DropdownMenu inside the open Content
- Focus management on open / close / sub navigation
## Talla y tema
**28 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `context-menu`).
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--context-menu-…`) | Valor por defecto |
| ---------------------------- | -------------------------------- |
| `content-fg` | `var(--color-content-primary)` |
| `item-fg` | `var(--color-content-primary)` |
| `item-line-height` | `var(--leading-ui)` |
| `chevron-stroke` | `var(--border-width-medium)` |
| `heading-fg` | `var(--color-content-muted)` |
| `heading-font-weight` | `var(--style-label-font-weight)` |
| `heading-letter-spacing` | `var(--tracking-caps)` |
| `open-trigger-outline-width` | `var(--border-width)` |
| `open-trigger-outline` | `var(--color-border-default)` |
| `open-trigger-radius` | `var(--radius-sm)` |
| `content-min-width` | `12rem` |
| `content-max-height` | `min(60vh, 22rem)` |
| `content-padding` | `var(--space-1)` |
| `content-radius` | `var(--radius-md)` |
| `content-font-size` | `var(--size-sm-font-size)` |
| `item-gap` | `var(--space-2)` |
| `item-height` | `2rem` |
| `item-padding-inline` | `var(--space-2)` |
| `item-padding-block` | `var(--space-1)` |
| `item-radius` | `var(--radius-sm)` |
| `hover-item-bg` | `var(--color-primary-element)` |
| `hover-item-fg` | `var(--color-content-primary)` |
| `item-disabled-opacity` | `0.55` |
| `heading-padding-inline` | `var(--space-2)` |
| `heading-padding-block` | `var(--space-1)` |
| `heading-font-size` | `var(--size-xs-font-size)` |
| `separator-bg` | `var(--color-neutral-separator)` |
| `separator-margin-block` | `var(--space-1)` |
## Comparativa
| Lib | Trigger source | Long-press (touch) | Shift+F10 | Sub-menus | Cursor anchor |

@ -82,6 +82,22 @@ WAI-ARIA Spinbutton pattern (per segment):
Soma owns: segment focus / arrow stepping per endpoint, paste
distribution, validation, hidden form inputs (ISO date strings).
## Talla y tema
**7 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `date-range-field`).
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--date-range-field-…`) | Valor por defecto |
| ------------------------------ | ------------------------------ |
| `column-gap` | `var(--space-3)` |
| `row-gap` | `var(--space-1-5)` |
| `label-fg` | `var(--color-content-primary)` |
| `label-font-size` | `var(--size-sm-font-size)` |
| `label-font-weight` | `var(--font-weight-regular)` |
| `separator-fg` | `var(--color-content-muted)` |
| `separator-glyph` | `"'—'"` |
## Comparativa
| Lib | Two endpoints | Shared validate | Segment scoping | Range tint |

@ -122,6 +122,43 @@ ContextMenu has its own recipe; Menubar's per-menu panel already carries
`data-dropdown-menu-content` and re-uses `DropdownMenu.Item`, so it inherits the
rhythm + the item preset and only adds the `[data-stagger]` marker.
## Talla y tema
**28 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `dropdown-menu`).
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--dropdown-menu-…`) | Valor por defecto |
| --------------------------- | --------------------------------- |
| `content-z` | `var(--z-index-overlay-floating)` |
| `content-fg` | `var(--color-content-primary)` |
| `item-fg` | `var(--color-content-primary)` |
| `item-line-height` | `var(--leading-ui)` |
| `chevron-stroke` | `var(--border-width-medium)` |
| `chevron-size` | `0.625rem` |
| `heading-fg` | `var(--color-content-muted)` |
| `heading-font-weight` | `var(--style-label-font-weight)` |
| `heading-letter-spacing` | `var(--tracking-caps)` |
| `separator-thickness` | `var(--border-width)` |
| `content-min-width` | `12rem` |
| `content-max-height` | `min(60vh, 22rem)` |
| `content-padding` | `var(--space-1)` |
| `content-radius` | `var(--radius-md)` |
| `content-font-size` | `var(--size-sm-font-size)` |
| `item-gap` | `var(--space-2)` |
| `item-height` | `2rem` |
| `item-padding-inline` | `var(--space-2)` |
| `item-padding-block` | `var(--space-1)` |
| `item-radius` | `var(--radius-sm)` |
| `hover-item-bg` | `var(--color-primary-element)` |
| `hover-item-fg` | `var(--color-content-primary)` |
| `item-disabled-opacity` | `0.55` |
| `heading-padding-inline` | `var(--space-2)` |
| `heading-padding-block` | `var(--space-1)` |
| `heading-font-size` | `var(--size-xs-font-size)` |
| `separator-bg` | `var(--color-neutral-separator)` |
| `separator-margin-block` | `var(--space-1)` |
## Comparativa
| Lib | Sub-menus | CheckboxItem / RadioItem | CheckboxGroup | Arrow | Modal toggle |

@ -26,6 +26,41 @@ reference floor fixed by the research dossier
the title reads ONE discrete bundle size above the component size
(visual hierarchy decoupled from the semantic `level`).
## Talla y tema
**26 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `empty-state`), con sus pasos por talla y el nombre RESUELTO que consume la receta.
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--empty-state-…`) | Valor por defecto |
| -------------------------- | --------------------------------- |
| `gap` | `var(--space-3)` |
| `actions-gap` | `var(--space-2)` |
| `media-bg` | `var(--color-neutral-track)` |
| `media-fg` | `var(--color-content-muted)` |
| `media-radius` | `var(--radius-lg)` |
| `description-measure` | `45ch` |
| `padding-block-sm` | `var(--space-4)` |
| `padding-block-md` | `var(--space-8)` |
| `padding-block-lg` | `var(--space-12)` |
| `padding-inline-sm` | `var(--space-3)` |
| `padding-inline-md` | `var(--space-4)` |
| `padding-inline-lg` | `var(--space-5)` |
| `title-font-size-sm` | `var(--size-md-font-size)` |
| `title-font-size-md` | `var(--size-lg-font-size)` |
| `title-font-size-lg` | `var(--size-xl-font-size)` |
| `description-font-size-sm` | `var(--size-sm-font-size)` |
| `description-font-size-md` | `var(--size-md-font-size)` |
| `description-font-size-lg` | `var(--size-md-font-size)` |
| `media-glyph-size-sm` | `var(--size-sm-icon-size)` |
| `media-glyph-size-md` | `var(--size-lg-icon-size)` |
| `media-glyph-size-lg` | `var(--size-xl-icon-size)` |
| `title-font-weight` | `var(--font-weight-semibold)` |
| `title-line-height` | `var(--size-sm-font-line-height)` |
| `title-fg` | `var(--color-content-primary)` |
| `description-line-height` | `var(--size-md-font-line-height)` |
| `description-fg` | `var(--color-content-muted)` |
## Comparativa
| Ref | Anatomy | What we adopted | What we didn't |

@ -54,6 +54,43 @@ alinearse con el contrato Soma/Morfo.
| Visual orientation | Eidos | `vertical`, `horizontal`, `responsive` |
| Intent color | Eidos | Focus/required accent; invalid sigue usando estado de error |
## Talla y tema
**68 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `field`), con sus pasos por talla y el nombre RESUELTO que consume la receta.
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Grupo | Claves |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `escalas` | `gap-xs` · `gap-sm` · `gap-md` · `gap-lg` · `gap-xl` · `control-height-xs` · `control-height-sm` · `control-height-md` · `control-height-lg` · `control-height-xl` · `control-padding-inline-xs` · `control-padding-inline-sm` · `control-padding-inline-md` · `control-padding-inline-lg` · `control-padding-inline-xl` · `control-gap-xs` · `control-gap-sm` · `control-gap-md` · `control-gap-lg` · `control-gap-xl` · `control-font-size-xs` · `control-font-size-sm` · `control-font-size-md` · `control-font-size-lg` · `control-font-size-xl` |
| `raíz` | `font-family` · `floating-label-top` · `floating-label-padding-inline` · `layout-column-gap` · `required-fg` · `optional-fg` · `optional-font-weight` · `control-line-height` · `control-font-weight` · `control-radius` · `control-border-width` · `control-border` · `control-bg` · `control-ghost-border` · `control-ghost-bg` · `control-bg-readonly` · `control-fg` · `control-placeholder-fg` · `affix-fg` · `control-trigger-size` · `control-trigger-radius` · `control-trigger-fg` · `message-line-height` · `helper-fg` · `error-fg` · `transition-duration` · `transition-ease` |
| `label` | `label-gap` · `label-font-weight` · `label-line-height` · `label-fg` |
| `estado` | `invalid-label-fg` · `disabled-label-fg` · `hover-control-border` · `focus-control-border` · `invalid-control-border` · `disabled-control-bg` · `hover-control-trigger-fg` · `focus-control-trigger-fg` · `disabled-opacity` |
| `segment` | `segment-height` · `segment-active-bg` · `segment-active-text` |
**Lo que el guard R-5.4 da por silencioso, con su razón medida**
(`scripts/theming-sentinel-exceptions.ts`):
- `invalid-label-fg` — only under [data-invalid] and the demo boots valid; forced -> reaches (oklch(0.5855 0.1743 42.74) -> rgb(1,2,3))
- `disabled-label-fg` — only under [data-disabled]; forced -> reaches (oklch(0.7921 0 0) -> rgb(1,2,3))
- `invalid-control-border` — only under [data-invalid]; forced -> reaches (oklch(0.8059 0.1123 59.96) -> rgb(1,2,3))
- `disabled-control-bg` — only under [data-disabled]; forced -> reaches (oklch(0.9551 0 0) -> rgb(1,2,3))
- `control-bg-readonly` — only under [data-readonly]; forced -> reaches (oklch(0.9551 0 0) -> rgb(1,2,3))
- `disabled-opacity` — it lives on the CONTROL, not on the field root, so the guard read the wrong node; forced data-disabled and read on the control -> reaches (0.4 -> 0.137)
- `control-ghost-border` — the ghost variant and the demo boots surface; forced data-variant=ghost -> reaches (transparent -> rgb(1,2,3))
- `control-ghost-bg` — same ghost variant; forced -> reaches (transparent -> rgb(1,2,3))
- `layout-column-gap` — only in the horizontal orientation; forced data-orientation=horizontal -> reaches (16px -> 1234px)
- `focus-control-border` — only under :focus-within, and the guard blurs ON PURPOSE (the click-focus poisoning fix); focused from the keyboard -> reaches (the focus ring colour -> rgb(1,2,3))
- `focus-control-trigger-fg` — only under :focus-visible on the picker trigger, which the guard blurs; focused from the keyboard on /date-picker -> reaches (oklch(0.2435 0 0) -> rgb(1,2,3))
- `floating-label-top` — floatingLabel is a demo switch that boots OFF, and the label must also be floated (focused or filled); switch on + focus -> reaches (0px -> 1234px)
- `floating-label-padding-inline` — same switch; turned on -> reaches (4px -> 1234px)
- `error-fg` — the error text only RENDERS when the field is invalid, and it is the demo switch that mounts it - forcing the data-invalid attribute does not make Svelte render the node; switch on -> reache…
- `control-placeholder-fg` — the ::placeholder box exists only when the input HAS a placeholder attribute, and the demo input has none: getComputedStyle then returns the ELEMENT style and the token reads dead. With a pl…
- `segment-active-bg` — the ACTIVE segment being edited: no segment on the field route and none focused; measured on /date-field with the segment clicked -> reaches (the 40% mix -> rgb(1,2,3))
- `segment-active-text` — same segment; measured on /date-field -> reaches (oklch(0.5168 0.1733 305.88) -> rgb(1,2,3))
- `segment-height` — a FAMILY token that field OWNS and the pickers CONSUME - nothing on the field routes reads it - and it is RESOLVED (declared per [data-field][data-size]), so a :root write loses BY DESIGN: a…
- `transition-duration` — the guard freezes transitions to measure and this token IS the transition; measured unfrozen -> reaches (0.12s -> 11.5s)
## Comparativa externa
| Plataforma | Modelo | Eidos/UIX |

@ -45,6 +45,44 @@ estado + passthrough nativo completo.
`placeholderColor` (fondo mientras carga), `width`/`height`.
- Textos localizados: `loading` / `error` para AT.
## Talla y tema
**23 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `image`), con sus pasos por talla y el nombre RESUELTO que consume la receta.
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--image-…`) | Valor por defecto |
| ---------------------- | -------------------------------- |
| `size-xs` | `64px` |
| `size-sm` | `96px` |
| `size-md` | `160px` |
| `size-lg` | `240px` |
| `size-xl` | `360px` |
| `radius-none` | `0` |
| `radius-sm` | `var(--radius-sm)` |
| `radius-md` | `var(--radius-md)` |
| `radius-lg` | `var(--radius-lg)` |
| `radius-full` | `var(--radius-full)` |
| `fade-duration` | `var(--duration-normal)` |
| `fade-ease` | `var(--ease-out)` |
| `placeholder-base` | `var(--color-surface-raised)` |
| `skeleton-bg` | `var(--gradient-shimmer)` |
| `shimmer-duration` | `1.8s` |
| `font-family` | `var(--style-label-font-family)` |
| `fallback-fg` | `var(--color-content-secondary)` |
| `fallback-font-size` | `var(--size-sm-font-size)` |
| `fallback-line-height` | `var(--leading-ui)` |
| `error-bg` | `var(--color-surface-raised)` |
| `error-fg` | `var(--color-content-muted)` |
| `error-font-size` | `var(--size-sm-font-size)` |
| `error-line-height` | `var(--leading-ui)` |
**Lo que el guard R-5.4 da por silencioso, con su razón medida**
(`scripts/theming-sentinel-exceptions.ts`):
- `fade-duration` — the guard freezes transitions and this token IS the fade; measured unfrozen 2026-08-23 -> reaches (0.18s -> 11.5s)
- `fade-ease` — same instrument limit; measured unfrozen -> reaches (cubic-bezier(0, 0, 0.2, 1) -> steps(7))
## Comparativa
| Capacidad | UIX | Radix Avatar | Chakra Image | Next/Image |

@ -53,6 +53,20 @@ las partes internas por-menú ya no son propias — son re-exports de
entre triggers; Home/End extremos; dentro del menú rige el teclado de
DropdownMenu.
## Talla y tema
**35 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `menubar`), con sus pasos por talla y el nombre RESUELTO que consume la receta.
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Grupo | Claves |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `raíz` | `bg` · `border-fg` · `border-width` · `radius` |
| `escalas` | `gap-sm` · `gap-md` · `gap-lg` · `padding-sm` · `padding-md` · `padding-lg` · `height-sm` · `height-md` · `height-lg` |
| `trigger` | `trigger-fg` · `trigger-font-family` · `trigger-font-weight` · `trigger-line-height` · `trigger-font-size-sm` · `trigger-font-size-md` · `trigger-font-size-lg` · `trigger-padding-inline-sm` · `trigger-padding-inline-md` · `trigger-padding-inline-lg` |
| `estado` | `open-trigger-bg` · `open-trigger-fg` · `disabled-trigger-fg` |
| `panel` | `panel-z` · `panel-min-width` · `panel-max-height` · `panel-padding` · `panel-radius` · `panel-fg` · `panel-font-size-sm` · `panel-font-size-md` · `panel-font-size-lg` |
## Comparativa
| Capacidad | UIX | Radix | Bits UI | shadcn | React Aria |

@ -161,6 +161,42 @@ APG (el morfo lo cita): trigger `aria-haspopup` → `role="menu"` de
`menuitem`s; la geometría radial y el color por profundidad salen de los
engines del framework.
## Talla y tema
**19 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `onion-menu`).
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--onion-menu-…`) | Valor por defecto |
| ------------------------- | -------------------------------- |
| `trigger-lift` | `var(--fab-lift)` |
| `trigger-shadow` | `var(--fab-shadow)` |
| `trigger-radius` | `var(--radius-full)` |
| `trigger-fg` | `var(--color-content-on-solid)` |
| `sector-shadow` | `var(--shadow-raised)` |
| `sector-ring-fg` | `var(--color-content-on-solid)` |
| `sector-ring-width` | `2.5` |
| `sector-hover-ring-width` | `2` |
| `label-font-family` | `var(--style-label-font-family)` |
| `label-font-weight` | `var(--style-label-font-weight)` |
| `glyph-size` | `42%` |
| `glyph-bar-thickness` | `2px` |
| `glyph-bar-radius` | `var(--radius-full)` |
| `muted-opacity` | `0.85` |
| `hover-ring-opacity` | `0.65` |
| `rim-width` | `2` |
| `rim-glow-width` | `9` |
| `rim-glow-opacity` | `var(--opacity-45)` |
| `rim-glow-blur` | `var(--blur-sm)` |
**Lo que el guard R-5.4 da por silencioso, con su razón medida**
(`scripts/theming-sentinel-exceptions.ts`):
- `trigger-lift` — the lift TRANSITIONS, so an immediate read after the write returns the old value; hovered with transitions frozen -> reaches (translate 0 -2px -> 0 -123px)
- `rim-width` — the luminous look is not rendered by the component demo; measured on /active/docs/agnt -> reaches (2px -> 17px)
- `rim-glow-width` — same look, same route -> reaches (9px -> 17px)
- `rim-glow-blur` — same look, same route -> reaches (blur(4px) -> blur(13px))
## Comparativa
| Capacidad | UIX | Pie menus (Blender/juegos) | Marking menus | Radix/Ark/RAC |

@ -11,6 +11,34 @@ Themeable, accessible QR code — **own zero-dependency encoder**, framework
No Air baseline — new component (2026-06-12). Nothing in the legacy layers
rendered QR codes; the encoder itself is also new (`$libs/qr`, zero-dep).
## Talla y tema
**10 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `qr-code`), con sus pasos por talla y el nombre RESUELTO que consume la receta.
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--qr-code-…`) | Valor por defecto |
| --------------------- | ------------------ |
| `fg` | `#18181b` |
| `bg` | `#ffffff` |
| `size-xs` | `96px` |
| `size-sm` | `128px` |
| `size-md` | `160px` |
| `size-lg` | `200px` |
| `size-xl` | `256px` |
| `radius` | `var(--radius-md)` |
| `overlay-bg` | `#ffffff` |
| `overlay-radius` | `var(--radius-sm)` |
**Lo que el guard R-5.4 da por silencioso, con su razón medida**
(`scripts/theming-sentinel-exceptions.ts`):
- `size-xs` — one step per instance: the wrapper writes var(--qr-code-size-{k}) inline from the size prop and the demo runs lg. Forced -> reaches (96px -> 123px)
- `size-sm` — same, one step per instance
- `size-md` — same, one step per instance
- `size-xl` — same, one step per instance
- `fg` — painted as the SVG fill of the module path, not as color on the root; read on the path -> reaches (rgb(24,24,27) -> rgb(1,2,3))
## Comparativa — why a new one (vs the references)
| Capability | ark-ui (zag + `uqr`) | qr-code-styling | react-qr-code | **QrCode (ours)** |

@ -28,6 +28,25 @@ Origen: `air/components/layout/section`. Adaptación estándar.
`region` sólo cuando lleva `aria-label`/`aria-labelledby`, que es el default
correcto para una página que apila varias.
## Talla y tema
**4 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `section`), con sus pasos por talla y el nombre RESUELTO que consume la receta.
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--section-…`) | Valor por defecto |
| --------------------- | ----------------------------- |
| `padding-block-sm` | `var(--space-8)` |
| `padding-block-md` | `var(--space-12)` |
| `padding-block-lg` | `var(--space-16)` |
| `padding-block-xl` | `calc(var(--space-16) * 1.5)` |
**Lo que el guard R-5.4 da por silencioso, con su razón medida**
(`scripts/theming-sentinel-exceptions.ts`):
- `padding-block-sm` — one step per instance; the demo runs md. Forced the step inline -> reaches
- `padding-block-lg` — same, one step per instance
## Comparativa
| Capacidad | UIX | Radix Themes Section | Chakra | Mantine |

@ -34,6 +34,16 @@ components — nothing reinvented:
icon-only Button) + the menu (`.Content` → `<SplitButton.Menu>`, `.Item`, …).
- **`<SvgChevron>`** — the default ▾ glyph (rotates while open).
## Talla y tema
**1 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `split-button`).
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--split-button-…`) | Valor por defecto |
| -------------------------- | ----------------- |
| `trigger-padding-inline` | `var(--space-1)` |
## Comparativa
| Capability | Fluent UI | Ant `Dropdown.Button` | PrimeReact | Bootstrap | MUI (recipe) | **UIX** |

@ -30,6 +30,16 @@ Reference floor + reflow doctrine: the research dossier
blesses; `uix.perf`'s reflow detector can prove the demo does zero
forced-reflow — a measurable quality claim no reference library makes.
## Talla y tema
**1 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `sticky`).
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--sticky-…`) | Valor por defecto |
| -------------------- | ----------------------- |
| `z-index` | `var(--z-index-sticky)` |
## Comparativa
| Ref | Equivalente | Qué adoptamos | Qué no |

@ -115,6 +115,45 @@ espacio de props de soma y clobbeara el estampado).
- **Custom color** (`data-color-custom`) — **diferir**: la vía existe en Card
si surge el caso real.
## Talla y tema
**25 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `surface`).
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--surface-…`) | Valor por defecto |
| --------------------- | --------------------------------- |
| `radius` | `var(--radius-lg)` |
| `neutral-track` | `var(--color-neutral-track)` |
| `neutral-solid` | `var(--color-neutral-solid)` |
| `neutral-contrast` | `var(--color-neutral-contrast)` |
| `primary-track` | `var(--color-primary-track)` |
| `primary-solid` | `var(--color-primary-solid)` |
| `primary-contrast` | `var(--color-primary-contrast)` |
| `secondary-track` | `var(--color-secondary-track)` |
| `secondary-solid` | `var(--color-secondary-solid)` |
| `secondary-contrast` | `var(--color-secondary-contrast)` |
| `affirm-track` | `var(--color-affirm-track)` |
| `affirm-solid` | `var(--color-affirm-solid)` |
| `affirm-contrast` | `var(--color-affirm-contrast)` |
| `fulfill-track` | `var(--color-fulfill-track)` |
| `fulfill-solid` | `var(--color-fulfill-solid)` |
| `fulfill-contrast` | `var(--color-fulfill-contrast)` |
| `risk-track` | `var(--color-risk-track)` |
| `risk-solid` | `var(--color-risk-solid)` |
| `risk-contrast` | `var(--color-risk-contrast)` |
| `threat-track` | `var(--color-threat-track)` |
| `threat-solid` | `var(--color-threat-solid)` |
| `threat-contrast` | `var(--color-threat-contrast)` |
| `loss-track` | `var(--color-loss-track)` |
| `loss-solid` | `var(--color-loss-solid)` |
| `loss-contrast` | `var(--color-loss-contrast)` |
**Lo que el guard R-5.4 da por silencioso, con su razón medida**
(`scripts/theming-sentinel-exceptions.ts`):
- **Por patrón** `/^(primary|secondary|neutral|affirm|fulfill|risk|threat|loss)-(track|solid|contrast)$/` — la cascada de paleta: el bloque genérico `[data-surface][data-color]` se emite el ÚLTIMO y gana por orden, así que la pintura sale del `--palette-*` global (§13, pendiente de firma).
## Comparativa
| | UIX Surface | Chakra `Box bg=` | Radix Themes | MD3 |

@ -63,6 +63,22 @@ Soma posee: composición de un TimeField real por endpoint (valor por slice,
placeholder controlado compartido para que un endpoint no mueva el ancla del
otro), validación de rango, flags mergeados con `Field.Provider` (OR).
## Talla y tema
**7 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `time-range-field`).
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--time-range-field-…`) | Valor por defecto |
| ------------------------------ | ------------------------------ |
| `column-gap` | `var(--space-3)` |
| `row-gap` | `var(--space-1-5)` |
| `label-fg` | `var(--color-content-primary)` |
| `label-font-size` | `var(--size-sm-font-size)` |
| `label-font-weight` | `var(--font-weight-regular)` |
| `separator-fg` | `var(--color-content-muted)` |
| `separator-glyph` | `"'—'"` |
## Comparativa
| Lib | TimeRangeField dedicado | Endpoints segmentados | Validación de rango compartida |

@ -89,6 +89,16 @@ The group itself needs an accessible name (`aria-label` or
`aria-labelledby`) per APG. Soma warns at mount in dev when both are
missing.
## Talla y tema
**1 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `toggle-group`).
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--toggle-group-…`) | Valor por defecto |
| -------------------------- | ----------------- |
| `gap` | `var(--space-1)` |
## Comparativa
| Lib | Selection | Variant | Color | Attached | Block | Notes |

@ -7,6 +7,49 @@ Fecha de revision: 2026-05-17.
Air no tenia `tooltip` en `glm-5:src/uix/air/components`; este componente es
una envoltura visual sobre Soma/Morfo, no un port de Air.
## Talla y tema
**23 clave(s) pública(s)** en `lib/recipes/base.ts` (bloque `tooltip`), con sus pasos por talla y el nombre RESUELTO que consume la receta.
Es el contrato vivo: la pestaña **Tokens** de su demo lista estas mismas
claves y las resuelve sobre el escenario.
| Token (`--tooltip-…`) | Valor por defecto |
| ---------------------- | -------------------------------------------------------------------------- |
| `content-z` | `var(--z-index-overlay-tooltip)` |
| `max-width` | `22rem` |
| `padding-block-sm` | `var(--space-1)` |
| `padding-inline-sm` | `var(--space-2)` |
| `padding-block-md` | `var(--space-1-5)` |
| `padding-inline-md` | `var(--space-2-5)` |
| `padding-block-lg` | `var(--space-2)` |
| `padding-inline-lg` | `var(--space-3)` |
| `font-size-sm` | `var(--size-sm-font-size)` |
| `font-size-md` | `var(--size-md-font-size)` |
| `font-size-lg` | `var(--size-lg-font-size)` |
| `font-family` | `var(--style-label-font-family)` |
| `font-weight` | `var(--font-weight-regular)` |
| `line-height` | `var(--leading-text)` |
| `radius` | `var(--radius-md)` |
| `border-width` | `var(--border-width)` |
| `bg` | `var(--color-surface-overlay)` |
| `fg` | `var(--color-content-primary)` |
| `border` | `color-mix( in srgb, var(--color-border-default) 84%, var(--tooltip-bg) )` |
| `shadow` | `var(--depth-overlay-shadow), var(--depth-overlay-halo)` |
| `ghost-blur` | `var(--blur-md)` |
| `outline-arrow-bg` | `var(--color-surface-default)` |
| `outline-arrow-border` | `var(--color-border-strong)` |
**Lo que el guard R-5.4 da por silencioso, con su razón medida**
(`scripts/theming-sentinel-exceptions.ts`):
- `content-z` — it lands on the floating WRAPPER, which carries no data-tooltip-\* attr and so is outside the measured set; read on the wrapper -> reaches (90 -> 4321)
- `bg` — SUPERSEDED by the overlay plane: [data-depth=overlay] declares background at the same specificity and later in the cascade, so the recipe loses. Measured: --tooltip-bg moves nothing, --depth…
- `border` — same supersession: --depth-overlay-border repaints it, --tooltip-border does not
- `shadow` — same supersession: --depth-overlay-shadow repaints it, --tooltip-shadow does not
- `font-family` — same plane, the typography half of it (PLAN 12.9, unsigned)
- `line-height` — same plane, same open decision
- `outline-arrow-bg` — the arrow is painted through a SIBLING selector that needs the content carrying data-variant=outline AND the panel open; measured with the demo switch -> reaches (oklch(0.9911 0 0) -> rgb(1,…
## Comparativa
| Referencia | Superficie relevante | Estado Eidos |

@ -8,11 +8,13 @@
import MotionPanel from '../../lib/MotionPanel.svelte';
import SemaPanel from '../../lib/SemaPanel.svelte';
import { DemoTrace } from '../../lib/harness.svelte';
import TokensPanel from '../../lib/TokensPanel.svelte';
const uix = getActiveUix();
// prettier-ignore
type Tab = 'live' | 'system' | 'motion' | 'sema' | 'services' | 'api' | 'morfo' | 'recipe' | 'a11y';
type Tab = 'live' | 'system' | 'motion' | 'sema' | 'services' | 'api' | 'morfo' | 'recipe' | 'tokens'
| 'a11y';
let tab = $state<Tab>('live');
const trace = new DemoTrace();
@ -173,6 +175,9 @@
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>
Recipe
</button>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>12</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -350,6 +355,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="anchor-nav" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -8,8 +8,10 @@
} from '$uix/eidos/components/banner';
import { compileMorfo } from '$uix/morfo';
import { bannerMorfo } from '@/uix/morfo/components/banner';
import TokensPanel from '../../lib/TokensPanel.svelte';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens'
| 'a11y';
type TraceEntry = { event: string; family: string; intent?: string; at: number };
let tab = $state<Tab>('live');
let trace = $state<TraceEntry[]>([]);
@ -201,6 +203,9 @@
<span data-uix-tab-count>{events.length}</span>
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>53</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -453,6 +458,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="banner" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -9,8 +9,10 @@
import { Button } from '$uix/eidos/components/button';
import { compileMorfo } from '$uix/morfo';
import { buttonGroupMorfo } from '@/uix/morfo/components/button-group';
import TokensPanel from '../../lib/TokensPanel.svelte';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens'
| 'a11y';
type TraceEntry = { event: string; family: string; intent?: string; at: number };
let tab = $state<Tab>('live');
let trace = $state<TraceEntry[]>([]);
@ -184,6 +186,9 @@
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button
>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>1</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -452,6 +457,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="button-group" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -25,6 +25,7 @@
import MotionPanel from '../../lib/MotionPanel.svelte';
import SemaPanel from '../../lib/SemaPanel.svelte';
import { DemoTrace, PALETTE_SCALES } from '../../lib/harness.svelte';
import TokensPanel from '../../lib/TokensPanel.svelte';
const uix = getActiveUix();
@ -41,7 +42,8 @@
check: Icon.Check
} as const;
type Tab = 'live' | 'system' | 'motion' | 'sema' | 'services' | 'api' | 'morfo' | 'recipe' | 'a11y';
type Tab = 'live' | 'system' | 'motion' | 'sema' | 'services' | 'api' | 'morfo' | 'recipe' | 'tokens'
| 'a11y';
let tab = $state<Tab>('live');
const variants: ButtonVariant[] = ['solid', 'soft', 'surface', 'outline', 'ghost', 'plain'];
@ -218,6 +220,9 @@
<span data-uix-layer-badge="morfo">morfo</span><span data-uix-tab-count>{partsList.length}p · {actions.length}e</span>
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>106</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -469,6 +474,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="button" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -9,10 +9,12 @@
import MotionPanel from '../../lib/MotionPanel.svelte';
import SemaPanel from '../../lib/SemaPanel.svelte';
import { DemoTrace } from '../../lib/harness.svelte';
import TokensPanel from '../../lib/TokensPanel.svelte';
const uix = getActiveUix();
type Tab = 'live' | 'system' | 'motion' | 'sema' | 'services' | 'api' | 'morfo' | 'recipe' | 'a11y';
type Tab = 'live' | 'system' | 'motion' | 'sema' | 'services' | 'api' | 'morfo' | 'recipe' | 'tokens'
| 'a11y';
let tab = $state<Tab>('live');
// ── Stage + System axes ──────────────────────────────────────────────
@ -175,6 +177,9 @@
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>
Recipe
</button>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>34</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -478,6 +483,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="callout" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -11,11 +11,13 @@
import { getActiveUix } from '$active-uix';
import { page } from '$app/state';
import { colorValue, PALETTE_SCALES } from '../../lib/harness.svelte';
import TokensPanel from '../../lib/TokensPanel.svelte';
const uix = getActiveUix();
const params = page.url.searchParams;
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens'
| 'a11y';
let tab = $state<Tab>('live');
// ── State ──────────────────────────────────────────────────────────────
@ -263,6 +265,9 @@
<span data-uix-tab-count>{events.length}</span>
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>47</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -570,6 +575,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="checkbox" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -3,10 +3,12 @@
import { compileMorfo } from '$uix/morfo';
import { contextMenuMorfo } from '@/uix/morfo/components/context-menu';
import { getActiveUix } from '$active-uix';
import TokensPanel from '../../lib/TokensPanel.svelte';
const uix = getActiveUix();
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens'
| 'a11y';
let tab = $state<Tab>('live');
// ── Live state ────────────────────────────────────────────────────────
@ -331,6 +333,9 @@
<span data-uix-tab-count>{events.length}</span>
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>28</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -932,6 +937,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="context-menu" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -19,10 +19,12 @@
type HourCycle
} from '$libs/days';
import { colorValue, PALETTE_SCALES } from '../../lib/harness.svelte';
import TokensPanel from '../../lib/TokensPanel.svelte';
const uix = getActiveUix();
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens'
| 'a11y';
type TraceEntry = { event: string; family: string; intent?: string; at: number };
type Direction = 'ltr' | 'rtl';
type ValueProfile = 'date' | 'datetime' | 'zoned';
@ -462,6 +464,9 @@
<span data-uix-tab-count>{events.length}</span>
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>7</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -954,6 +959,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="date-range-field" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -3,10 +3,12 @@
import { compileMorfo } from '$uix/morfo';
import { dropdownMenuMorfo } from '@/uix/morfo/components/dropdown-menu';
import { getActiveUix } from '$active-uix';
import TokensPanel from '../../lib/TokensPanel.svelte';
const uix = getActiveUix();
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens'
| 'a11y';
let tab = $state<Tab>('live');
// ── Live state ────────────────────────────────────────────────────────
@ -361,6 +363,9 @@
<span data-uix-tab-count>{events.length}</span>
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>28</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -1043,6 +1048,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="dropdown-menu" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -15,10 +15,12 @@
import MotionPanel from '../../lib/MotionPanel.svelte';
import SemaPanel from '../../lib/SemaPanel.svelte';
import { DemoTrace } from '../../lib/harness.svelte';
import TokensPanel from '../../lib/TokensPanel.svelte';
const uix = getActiveUix();
type Tab = 'live' | 'system' | 'motion' | 'sema' | 'services' | 'api' | 'morfo' | 'recipe' | 'a11y';
type Tab = 'live' | 'system' | 'motion' | 'sema' | 'services' | 'api' | 'morfo' | 'recipe' | 'tokens'
| 'a11y';
let tab = $state<Tab>('live');
// ── Stage + System axes ──────────────────────────────────────────────
@ -225,6 +227,9 @@
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>
Recipe
</button>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>26</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -522,6 +527,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="empty-state" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -8,8 +8,10 @@
} from '$uix/eidos/components/field';
import { compileMorfo } from '$uix/morfo';
import { fieldMorfo } from '@/uix/morfo/components/field';
import TokensPanel from '../../lib/TokensPanel.svelte';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens'
| 'a11y';
type FieldState = 'idle' | 'error' | 'disabled' | 'readonly' | 'loading';
type TraceEntry = { event: string; family: string; intent?: string; at: number };
@ -209,6 +211,9 @@
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}
>Recipe</button
>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>68</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -481,6 +486,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="field" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -12,8 +12,10 @@
import { AspectRatio } from '$uix/eidos/components/aspect-ratio';
import { compileMorfo } from '$uix/morfo';
import { imageMorfo } from '@/uix/morfo/components/image';
import TokensPanel from '../../lib/TokensPanel.svelte';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens'
| 'a11y';
type TraceEntry = { event: string; family: string; intent?: string; at: number };
let tab = $state<Tab>('live');
let trace = $state<TraceEntry[]>([]);
@ -177,6 +179,9 @@
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button
>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>23</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -898,6 +903,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="image" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -9,10 +9,12 @@
import { compileMorfo } from '$uix/morfo';
import { menubarMorfo } from '@/uix/morfo/components/menubar';
import { getActiveUix } from '$active-uix';
import TokensPanel from '../../lib/TokensPanel.svelte';
const uix = getActiveUix();
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens'
| 'a11y';
let tab = $state<Tab>('live');
// ── Live state ────────────────────────────────────────────────────────
@ -433,6 +435,9 @@
<span data-uix-tab-count>{events.length}</span>
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>35</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -752,6 +757,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="menubar" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -11,10 +11,12 @@
import { compileMorfo } from '$uix/morfo';
import { onionMenuMorfo } from '@/uix/morfo/components/onion-menu';
import { getActiveUix } from '$active-uix';
import TokensPanel from '../../lib/TokensPanel.svelte';
const uix = getActiveUix();
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens'
| 'a11y';
type TraceEntry = { event: string; family: string; intent?: string; at: number };
let tab = $state<Tab>('live');
let trace = $state<TraceEntry[]>([]);
@ -253,6 +255,9 @@
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button
>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>19</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -572,6 +577,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="onion-menu" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -7,8 +7,10 @@
} from '$uix/eidos/components/qr-code';
import { compileMorfo } from '$uix/morfo';
import { qrCodeMorfo } from '@/uix/morfo/components/qr-code';
import TokensPanel from '../../lib/TokensPanel.svelte';
type Tab = 'live' | 'examples' | 'api' | 'morfo' | 'recipe' | 'a11y';
type Tab = 'live' | 'examples' | 'api' | 'morfo' | 'recipe' | 'tokens'
| 'a11y';
let tab = $state<Tab>('live');
// The encoder takes ANY UTF-8 string — not just URLs. The reader decides how
@ -130,6 +132,9 @@
<span data-uix-tab-count>{partsList.length}p</span>
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>10</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -263,6 +268,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="qr-code" />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -3,8 +3,10 @@
import { Box } from '$uix/eidos/components/box';
import { compileMorfo } from '$uix/morfo';
import { sectionMorfo } from '@/uix/morfo/components/section';
import TokensPanel from '../../lib/TokensPanel.svelte';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens'
| 'a11y';
type TraceEntry = { event: string; family: string; intent?: string; at: number };
let tab = $state<Tab>('live');
let trace = $state<TraceEntry[]>([]);
@ -149,6 +151,9 @@
<span data-uix-tab-count>{events.length}</span>
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>4</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -325,6 +330,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="section" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -7,8 +7,10 @@
} from '$uix/eidos/components/split-button';
import { compileMorfo } from '$uix/morfo';
import { splitButtonMorfo } from '@/uix/morfo/components/split-button';
import TokensPanel from '../../lib/TokensPanel.svelte';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens'
| 'a11y';
type TraceEntry = { event: string; family: string; intent?: string; at: number };
let tab = $state<Tab>('live');
let trace = $state<TraceEntry[]>([]);
@ -193,6 +195,9 @@
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button
>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>1</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -473,6 +478,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="split-button" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -8,11 +8,13 @@
import MotionPanel from '../../lib/MotionPanel.svelte';
import SemaPanel from '../../lib/SemaPanel.svelte';
import { DemoTrace } from '../../lib/harness.svelte';
import TokensPanel from '../../lib/TokensPanel.svelte';
const uix = getActiveUix();
// prettier-ignore
type Tab = 'live' | 'system' | 'motion' | 'sema' | 'services' | 'api' | 'morfo' | 'recipe' | 'a11y';
type Tab = 'live' | 'system' | 'motion' | 'sema' | 'services' | 'api' | 'morfo' | 'recipe' | 'tokens'
| 'a11y';
let tab = $state<Tab>('live');
// ── Stage + System axes ──────────────────────────────────────────────
@ -192,6 +194,9 @@
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>
Recipe
</button>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>1</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -397,6 +402,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="sticky" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -8,6 +8,7 @@
import { compileMorfo } from '$uix/morfo';
import { timeRangeFieldMorfo } from '@/uix/morfo/components/time-range-field';
import { getActiveUix } from '$active-uix';
import TokensPanel from '../../lib/TokensPanel.svelte';
import {
Time,
type TimeValue,
@ -16,7 +17,8 @@
type HourCycle
} from '$libs/days';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens'
| 'a11y';
type TraceEntry = { event: string; family: string; intent?: string; at: number };
type Direction = 'ltr' | 'rtl';
@ -239,6 +241,9 @@
<span data-uix-tab-count>{events.length}</span>
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>7</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -488,6 +493,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="time-range-field" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -12,11 +12,13 @@
import { getActiveUix } from '$active-uix';
import { page } from '$app/state';
import { colorValue, PALETTE_SCALES } from '../../lib/harness.svelte';
import TokensPanel from '../../lib/TokensPanel.svelte';
const uix = getActiveUix();
const params = page.url.searchParams;
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens'
| 'a11y';
let tab = $state<Tab>('live');
// ── Live state ──────────────────────────────────────────────────
@ -234,6 +236,9 @@
<span data-uix-tab-count>{events.length}</span>
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>1</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -707,6 +712,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="toggle-group" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

@ -5,12 +5,14 @@
import { getActiveUix } from '$active-uix';
import type { Side, Align } from '$soma/layers/floating';
import { page } from '$app/state';
import TokensPanel from '../../lib/TokensPanel.svelte';
const uix = getActiveUix();
const params = page.url.searchParams;
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens'
| 'a11y';
type TraceEntry = { event: string; family: string; intent?: string; at: number };
let tab = $state<Tab>('live');
let trace = $state<TraceEntry[]>([]);
@ -272,6 +274,9 @@
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}
>Recipe</button
>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>23</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -901,6 +906,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="tooltip" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>

Loading…
Cancel
Save

Powered by TurnKey Linux.