You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/continue.md

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.css unifica los ::before indicators 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 su padding-inline-start calc gane al padding-inline shorthand del row a igual specificity. Documentado en el propio partial.

  • #127 — modal prop 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 exponen modal por 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 soma child snippet:

    Consumer Variant set narrowed Default Color Notas
    DropdownMenu.Trigger ControlVariant (3) surface inherits —
    AlertDialog.Cancel ControlVariant (3) outline neutral hardcoded neutral para contrastar con Action destructive
    AlertDialog.Action SelectionVariant (3) solid — intent forwarded del Dialog vía child snippet props (sin CSS ancestor cascade)
    Combobox.ClearButton ControlVariant (3) ghost neutral iconOnly X via icon snippet
    SearchField.ClearTrigger ControlVariant (3) ghost neutral iconOnly X via icon snippet

    En 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) lee DialogProvider.opts.intent y lo pasa por child({ 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"> con ControlVariant.

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).

Powered by TurnKey Linux.