7.3 KiB
Continue tomorrow
Fecha de corte: 2026-05-23 (segunda mitad). Rama: active-uix.
Resumen del día
Segundo sprint sobre la rama: Tasks #118 + #120 + #127 completadas.
Patrón canonical para consumers de <Button> establecido y aplicado en 5
consumers. svelte-check: 0 errores en cada commit.
Commits del día (orden cronológico):
| Commit | Scope |
|---|---|
0e9628b8 |
refactor(eidos): extract shared menu-item indicator partial (#118) |
4b5bd9f3 |
refactor(dropdown-menu): remove modal; Trigger→Button; canon vocab fixes (#120 + #127) |
a2709476 |
style(menu-indicator): align with Checkbox / RadioGroup visual |
c6f063dc |
refactor(alert-dialog): Action/Cancel render <Button>; intent flows via snippet |
c98510af |
demo(alert-dialog): expose variant/size chips for Action and Cancel |
32598d7c |
fix(alert-dialog): narrow Action.variant to canonical SelectionVariant |
0b55be5c |
refactor(combobox + search-field): ClearButton/Clear render <Button> |
Hecho:
-
#118 — Shared menu-indicator partial.
src/uix/eidos/lib/menu-indicator.cssunifica los::beforeindicators de[data-{dropdown,context}-menu-{checkbox,radio}-item]. Geometría compartida, tokens privados (--_menu-indicator-{size,radius,color,glyph,border,inset}). Estilo final igual que<Checkbox>/<RadioGroup>standalone:- off → caja/círculo vacío con borde gris
- on → relleno púrpura primary + ✓ blanco (mask-image con el mismo path
SVG que
<SvgCheck>) / punto blanco interior (radio) - padding-inline-start reservado 26px → no shift al togglear
Crítico: el partial debe importarse DESPUÉS de los recipes de menu (en
eidos/index.css) para que supadding-inline-startcalc gane alpadding-inlineshorthand del row a igual specificity. Documentado en el propio partial. -
#127 —
modalprop eliminado de DropdownMenu. El prop estaba declarado en soma pero nunca cableado (sin overlay, sin scroll lock, sin pointer-events blocker). ark-ui / Headless-UI / Chakra Menu no exponenmodalpor convención — un menú no es un Dialog. Eliminado de soma (types + provider + component- test fixture + menubar consumer), eidos doc, demo (state + control + snippet
- API row + parity row), COMPONENT_GUIDE actualizado.
-
#120 — 5 consumers refactored a
<Button>vía somachildsnippet:Consumer Variant set narrowed Default Color Notas DropdownMenu.TriggerControlVariant(3)surface inherits — AlertDialog.CancelControlVariant(3)outline neutral hardcoded neutral para contrastar con Action destructive AlertDialog.ActionSelectionVariant(3)solid — intent forwarded del Dialog vía child snippet props (sin CSS ancestor cascade) Combobox.ClearButtonControlVariant(3)ghost neutral iconOnly X via iconsnippetSearchField.ClearTriggerControlVariant(3)ghost neutral iconOnly X via iconsnippetEn cada caso: per-recipe CSS chrome eliminada (border, padding, focus ring, hover, dimensions). Solo permanece CSS específico del comportamiento del componente (
[data-empty]para esconder clear buttons).AlertDialog.Action expone un mecanismo nuevo del soma: el provider (
AlertDialogActionProvider.dialogIntent) leeDialogProvider.opts.intenty lo pasa porchild({ props, intent }). El eidos wrapper lo forwardea a Button:intent={intentOverride ?? dialogIntent}. Cero CSS ancestor cascade.
Patrón canonical para consumers de <Button> (aplicar a futuros)
Regla 1 — Variant siempre narrowed a un set canónico en
src/uix/eidos/lib/types.ts (4 sets, todos 3-4 valores):
export type ControlVariant = 'surface' | 'outline' | 'ghost'; // form controls
export type SelectionVariant = 'solid' | 'outline' | 'ghost'; // primary CTAs
export type ChipVariant = 'soft' | 'solid' | 'outline' | 'ghost';
export type MarkerVariant = 'solid' | 'soft' | 'outline';
Nunca redeclarar; siempre import type { ... } from '$uix/eidos/lib/types'.
Si necesitas otro set canónico, defínelo ahí. Si necesitas restringir aún
más, usa Extract<ButtonVariant, CanonSet>.
Regla 2 — Chip parity §12.7. El demo muestra TODOS los valores del set narrowed. No truncar. No mezclar.
Regla 3 — Composición vía soma child snippet. El wrapper eidos no
crea <button>; usa el child snippet del componente headless de soma:
<Soma.Part {...rest}>
{#snippet child({ props })}
{#if outerChild}
{@render outerChild({ props })}
{:else}
<Button {...props} {variant} {size} {intent} {color} {iconOnly}>
{@render children?.()}
</Button>
{/if}
{/snippet}
</Soma.Part>
Nota: si el wrapper tiene un prop llamado child, renombra en destructure
a outerChild — el {#snippet child} shadow infinite-loops si no.
Regla 4 — Contracts cross-layer. Si el dialog/popover/menu padre tiene
estado evaluativo (intent, color, status) que el Button hijo debe heredar,
el soma provider del Button-like part lo expone via child({ props, ... })
mediante WithChildNoChildrenSnippetProps para que children siga siendo
un Snippet plano:
// soma types.ts
export type FooButtonProps = WithChildNoChildrenSnippetProps<
{ id?: string },
{ intent: 'neutral' | 'risk' | 'threat' } // extra snippet props
>;
// soma provider.svelte.ts
readonly dialogIntent = $derived.by(() => this.dialog.opts.intent.current);
// soma component
{@render child({ props: mergedProps, intent: state.dialogIntent })}
Regla 5 — CSS recipe local solo para comportamiento. Después del
refactor, la recipe local solo debe contener selectores específicos del
componente ([data-foo][data-empty] [data-foo-clear], data-state='open'
transitions). Chrome general (border, padding, focus ring, hover) es de
Button.
Regla 6 — Verificación obligatoria: svelte-check 0 errores + browser
visual confirm (mcp__Claude_Preview__preview_eval con getComputedStyle).
Pendiente
Sin tareas explícitas
Sprint cerrado limpio. Próximos posibles candidatos (no urgentes):
- NumberField stepper buttons → decisión consciente de NO refactorizar
(mantienen comportamiento bespoke
hold-to-repeat). Re-evaluar si se necesita variant/size unification. - Dialog.Trigger / Popover.Trigger / Drawer.Trigger → mismo refactor que DropdownMenu.Trigger podría aplicar. No estaban en el scope #120.
- Select.Trigger / Combobox.Trigger → form controls; podrían adoptar
<Button variant="surface">conControlVariant.
Audit walk
Considerar pasar npm run component:audit para detectar drift en otros
recipes después de los cambios (CSS recipes que apuntaban a tokens
Toggle-derived ahora pueden tener selectors orphan).
Comandos útiles
cd G:/dev/svelte/vicen
npm run check # svelte-check (0 errors expected)
npm run dev # via .claude/launch.json "dev"
npm run generate:eidos-css # if recipe tokens change
npm run component:audit # static contract audit
Estado del working tree
Limpio. Branch active-uix con 7 commits nuevos sobre el HEAD anterior de
hoy (dc03773c). Sin push pendiente (decisión del usuario cuando quiera
empujar a gita/active-uix).