Closes the deuda left explicit in hand-off 2026-05-27 #5: the inlined
derivation expressions in toggle-group.css duplicated Toggle's recipe
because [data-toggle-group-item] wasn't a descendant of [data-toggle].
Resolution is structural, not a drift test:
- morfo: each item declares { attr: 'data-toggle', value: v.literal(''),
severity: 'required' }. Captures structural identity — an item IS a
toggle in every observable sense (same press, same variant/color/size,
same on/off machine).
- eidos: new context.ts propagates the root's variant + size via
reactive getters; the item wrapper writes them as data-attrs on its
button. Combined with data-toggle, the item is DOM-equivalent to a
standalone <Toggle>.
- eidos css: ~150 lines deleted (base derived tokens, variant cascades,
size cascade, focus/disabled/icon-only duplicates). Toggle's recipe
now paints the item end-to-end. CSS keeps only the grouping concerns:
flex, orientation, attached, block, focus z-index, group disabled.
- composition (TSC v2.2) stays — palette overrides land on the item
under [data-toggle-group][data-color='X'] [data-toggle-group-item],
which Toggle's --toggle-palette-* chain reads at item scope.
Verification:
- DOM probe (12 combinations: 4 colors × 3 variants × 2 states) on
/uix/components/toggle-group. Computed values match bit-a-bit with
baseline pre-refactor (e.g. affirm/solid on = rgb(18,165,148),
risk/outline on = srgb(0.2,0.118,0.043), threat/ghost on =
rgb(25,17,17)).
- vitest src/uix/{morfo,eidos}: 163/163 pass.
- vitest src/uix/soma/components/toggle-group: 4/4 pass.
- npm run check: 12 errors (all pre-existing in active Words sprint;
zero added by this migration).
Drive-by: the same npm-check pass surfaced 4 stale 'orientation' refs
in words-toolbar.svelte + words-toolbar-group.svelte from this session's
earlier toolbar refactor (orientation was removed from types but not
from these soma components). Removed; brings check from 16 → 12 errors.
CLAUDE.md hand-off 2026-05-28 documents the new doctrine: when a
wrapper visually reuses another, declare structural identity in the
wrapper's morfo. Don't duplicate the cascade, don't extend TSC for one
case.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cierra la deuda dejada explícitamente abierta en el hand-off 2026-05-27 #5 ("Los `inlined derivation expressions` en `toggle-group.css` son la única duplicación entre recipes/base.ts y CSS. Si Toggle's derivations cambian, hay que actualizar ambos."). Ahora son cero.
**Pregunta arquitectónica del usuario** tras verificar el fix EXT-FIX (color cascade): "¿qué es lo recomendado en referencia al ecosistema y su diseño y arquitectura?". Respuesta firme: **resolver la duplicación estructuralmente, no con un test de drift**. El item de toggle-group ES un toggle (mismo press, mismo variant/color/size, misma máquina on/off) — la doctrina "morfo declara DNA" pide declararlo.
**Cambios**:
- `src/uix/morfo/components/toggle-group.ts`: el part `Item` declara `{ attr: 'data-toggle', value: v.literal(''), severity: 'required' }`. Cada item proyecta `data-toggle=""` como atributo de presencia. Cero overhead, captura la identidad estructural.
- `src/uix/eidos/components/toggle-group/context.ts` (nuevo): contexto Svelte tipado (`ToggleGroupEidosCtx` con getters reactivos para `variant` y `size`) — propaga las dos perillas eidos-only del root a cada item.
- `src/uix/eidos/components/toggle-group/toggle-group.svelte`: setea el contexto en el root. Getters mantienen reactividad cuando el prop cambia.
- `src/uix/eidos/components/toggle-group/toggle-group-item.svelte`: lee el contexto y escribe `data-variant={ctx?.variant}` + `data-size={ctx?.size}` en el button. Combinado con `data-toggle` del morfo, el elemento es DOM-equivalente a un `<Toggle>` standalone.
- `src/uix/eidos/components/toggle-group/toggle-group.css`: borradas ~150 líneas (todas las derivaciones base + variant cascade + size cascade + focus-visible / disabled / icon-only duplicados). El CSS conserva SOLO grouping concerns (flex layout, orientation, attached con first/last/border-radius, block, focus z-index, group-level disabled). El header comment se rescribe documentando la nueva división de responsabilidades.
- `src/uix/eidos/components/toggle-group/README.md`: sección "Recipe" + "Decisiones" actualizadas — la entrada "Structural identity" documenta el patrón.
**Por qué TSC v2.2 composition se queda en la mezcla**: la composition sigue siendo necesaria para overridear `--toggle-palette-*` en el item bajo `[data-toggle-group][data-color='X'] [data-toggle-group-item]`. El color NO se propaga via contexto porque la composition ya hace el trabajo a nivel CSS, sin overhead reactivo. Variant/size sí se propagan porque tienen muchos derivados (height, padding, gap, font, radius × 5 sizes; bg, fg, border, hover, on, on-hover × 3 variants) que solo Toggle's recipe ya emite — no había ningún beneficio en mantener cascadas paralelas.
**Verificación**: probe DOM en `/uix/components/toggle-group` capturando computed values en las 12 combinaciones (4 colores × 3 variants) × 2 estados (on/off) — match bit-a-bit con baseline pre-refactor. Ejemplos:
- `affirm/solid` on: `rgb(18,165,148)` = `#12a594` (palette-solid affirm)
- `threat/ghost` on: `rgb(25,17,17)` = `#191111` (palette-track threat)
**Tests**: 163/163 pass en `src/uix/morfo` + `src/uix/eidos`. 4/4 pass en `src/uix/soma/components/toggle-group`. `npm run check`: 16 errores pre-existentes (los mismos del sprint Words), CERO añadidos por esta migración.
**Coste real vs estimado**: el hand-off #5 estimó el cambio como "too big" — incorrecto. Total = 1 entrada en morfo + 1 archivo de contexto (~30 líneas) + 2 ediciones puntuales en wrappers (~5 líneas cada) + 1 rewrite de CSS reduciendo ~150 líneas a ~85. La parte engañosa era pensar que requería tocar Toggle's CSS — no lo hace.
**Doctrina reforzada**: cuando un wrapper componente reusa visualmente otro, la respuesta canónica NO es duplicar el cascade ni extender TSC con un tercer feature. Es declarar la identidad estructural en el morfo del wrapper. Patrón aplicable si emerge button-group, link-group, etc.