feat(eidos): Lote A closes — toast composes IconButton; close family defers to Button defaults

- toast-close now composes the canonical <IconButton rounded="full"> via soma's
  child snippet (keeps the round dismiss shape); removed the bespoke
  [data-toast-close] chrome + the orphaned --toast-close-* recipe tokens; the
  toast item grid still places it in the trailing column. aria-label routes
  through the common close label.
- Per the close-family decision: variant/color defer to Button's canonical
  defaults (solid/primary) across the family — no hardcoded ghost/neutral.
  banner-close dropped its ghost/neutral defaults to match popover/dialog/drawer.
- component-visual-attrs manifest: toast-close delegates icon-only to IconButton.

Verified in browser (toast close = round solid-primary ✕, correctly placed).
svelte-check + orphan test clean for touched files. Excludes words/palabras/chronos.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
active-uix
dev 4 months ago
parent 0a43225ec2
commit 7092450734

@ -76,7 +76,6 @@ const VISUAL_ATTRS = [
['tag-group/tag-group.svelte', ['data-size', 'data-variant', 'data-color']], ['tag-group/tag-group.svelte', ['data-size', 'data-variant', 'data-color']],
['tags-input/tags-input.svelte', ['data-size', 'data-variant', 'data-color']], ['tags-input/tags-input.svelte', ['data-size', 'data-variant', 'data-color']],
['toolbar/toolbar.svelte', ['data-size', 'data-variant']], ['toolbar/toolbar.svelte', ['data-size', 'data-variant']],
['toast/toast-close.svelte', ['data-icon-only']],
['toast/toast-viewport.svelte', ['data-position']], ['toast/toast-viewport.svelte', ['data-position']],
['toggle/toggle.svelte', ['data-size', 'data-variant', 'data-block', 'data-icon-only']], ['toggle/toggle.svelte', ['data-size', 'data-variant', 'data-block', 'data-icon-only']],
['tooltip/tooltip-content.svelte', ['data-size', 'data-variant']], ['tooltip/tooltip-content.svelte', ['data-size', 'data-variant']],

@ -4,8 +4,9 @@
* `<IconButton>` (eidos-only part — Banner has no soma close; the consumer * `<IconButton>` (eidos-only part — Banner has no soma close; the consumer
* wires `onclick` to their own visibility state). Inherits Button's chrome, * wires `onclick` to their own visibility state). Inherits Button's chrome,
* focus ring and disabled treatment; only the positioning * focus ring and disabled treatment; only the positioning
* (`margin-inline-start: auto`) stays in `banner.css`. Default `variant='ghost' * (`margin-inline-start: auto`) stays in `banner.css`. `variant` / `color`
* color='neutral'` — a subtle dismiss. The glyph defaults to ✕. * defer to Button's canonical defaults (`solid` / `primary`) — the consumer
* overrides per-usage for a subtler dismiss. The glyph defaults to ✕.
*/ */
import { ActiveEidos } from '$uix/eidos'; import { ActiveEidos } from '$uix/eidos';
import { IconButton } from '$uix/eidos/components/icon-button'; import { IconButton } from '$uix/eidos/components/icon-button';
@ -14,9 +15,9 @@
let { let {
'aria-label': ariaLabel, 'aria-label': ariaLabel,
variant = 'ghost', variant,
size, size,
color = 'neutral', color,
children, children,
...restProps ...restProps
}: BannerCloseProps = $props(); }: BannerCloseProps = $props();

@ -59,6 +59,10 @@ export type BannerProps = Omit<HTMLAttributes<HTMLElement>, 'children'> & {
export type BannerCloseProps = Omit<IconButtonProps, 'aria-label' | 'children'> & { export type BannerCloseProps = Omit<IconButtonProps, 'aria-label' | 'children'> & {
/** Accessible label for the dismiss button. @default localized 'Dismiss' */ /** Accessible label for the dismiss button. @default localized 'Dismiss' */
'aria-label'?: string; 'aria-label'?: string;
/** Glyph override (defaults to the ✕). */ /**
* Glyph override (defaults to the ✕). `variant` / `color` (inherited from
* IconButton) defer to Button's canonical defaults — override for a subtler
* dismiss.
*/
children?: Snippet; children?: Snippet;
}; };

@ -1,27 +1,41 @@
<script lang="ts"> <script lang="ts">
/**
* Eidos `Toast.Close` — composes the canonical `<IconButton>` via soma's
* `child` snippet (no bespoke close chrome — it inherits Button's chrome,
* focus ring and disabled). `rounded='full'` keeps the toast's round dismiss
* shape; `variant` / `color` defer to Button's canonical defaults. The
* accessible name routes through langs ("Cerrar"). A `child` snippet bypasses
* IconButton.
*/
import { ActiveEidos } from '$uix/eidos';
import * as Toast from '$soma/components/toast'; import * as Toast from '$soma/components/toast';
import { IconButton } from '$uix/eidos/components/icon-button';
import { SvgX } from '$uix/eidos/components/svg';
import type { ToastCloseProps } from './types'; import type { ToastCloseProps } from './types';
let { children, ...rest }: ToastCloseProps = $props(); let {
variant,
size,
color,
'aria-label': ariaLabel,
child: outerChild,
children,
...rest
}: ToastCloseProps = $props();
const eidos = ActiveEidos.require();
const resolvedSize = $derived(size ?? 'sm');
const closeLabel = $derived(ariaLabel ?? eidos.langs.ts('#?common.buttons.close|Close'));
</script> </script>
<Toast.Close {...rest} data-icon-only={!children ? '' : undefined}> <Toast.Close {...rest}>
{#if children} {#snippet child({ props })}
{@render children?.()} {#if outerChild}
{:else} {@render outerChild({ props })}
<svg {:else}
viewBox="0 0 24 24" <IconButton {...props} {variant} size={resolvedSize} {color} rounded="full" aria-label={closeLabel}>
width="14" {#if children}{@render children()}{:else}<SvgX />{/if}
height="14" </IconButton>
fill="none" {/if}
stroke="currentColor" {/snippet}
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
{/if}
</Toast.Close> </Toast.Close>

@ -299,36 +299,6 @@
var(--toast-action-focus-ring-color); var(--toast-action-focus-ring-color);
} }
[data-toast-close] { /* `Toast.Close` composes the canonical `<IconButton rounded="full">`, so its
display: inline-flex; chrome (size, bg, border, hover, focus ring) comes from button.css. The toast
align-items: center; item grid (above) places it in the trailing `auto` column. */
justify-content: center;
inline-size: var(--toast-close-size);
block-size: var(--toast-close-size);
flex: 0 0 auto;
padding: 0;
border: var(--toast-close-border-width) solid var(--toast-close-border);
border-radius: var(--toast-close-radius);
background: var(--toast-close-bg);
color: var(--toast-close-color);
cursor: pointer;
transition:
background var(--toast-transition-duration) var(--toast-transition-ease),
border-color var(--toast-transition-duration) var(--toast-transition-ease),
color var(--toast-transition-duration) var(--toast-transition-ease),
box-shadow var(--toast-transition-duration) var(--toast-transition-ease);
}
[data-toast-close]:hover {
background: var(--toast-close-hover-bg);
border-color: var(--toast-close-hover-border);
color: var(--toast-close-hover-color);
}
[data-toast-close]:focus-visible {
outline: none;
box-shadow:
0 0 0 var(--toast-close-focus-ring-offset) var(--_toast-surface),
0 0 0 calc(var(--toast-close-focus-ring-offset) + var(--toast-close-focus-ring-width))
var(--toast-close-focus-ring-color);
}

@ -12,6 +12,8 @@ import type {
ToastData, ToastData,
Toaster Toaster
} from '$soma/components/toast'; } from '$soma/components/toast';
import type { ResponsiveProp } from '$uix/eidos/lib/types';
import type { ButtonVariant, ButtonSize, ButtonProps } from '$uix/eidos/components/button';
/** /**
* Six anchored placements for the toast viewport. Maps 1:1 to * Six anchored placements for the toast viewport. Maps 1:1 to
@ -30,7 +32,14 @@ export type ToastProps = ProviderProps;
export type ToastTitleProps = TitleProps; export type ToastTitleProps = TitleProps;
export type ToastDescriptionProps = DescriptionProps; export type ToastDescriptionProps = DescriptionProps;
export type ToastActionProps = ActionProps; export type ToastActionProps = ActionProps;
export type ToastCloseProps = CloseProps; export type ToastCloseProps = CloseProps & {
/** Button visual variant — defers to Button's canonical default. */
variant?: ButtonVariant;
/** Button size of the close control. @default 'sm' */
size?: ResponsiveProp<ButtonSize>;
/** Hierarchical color — defers to Button's canonical default. */
color?: ButtonProps['color'];
};
export type ToastItemProps = ItemProps; export type ToastItemProps = ItemProps;
export type ToastStatusProps = StatusProps; export type ToastStatusProps = StatusProps;
export type ToastMainProps = MainProps; export type ToastMainProps = MainProps;

@ -3070,18 +3070,6 @@
--toast-action-focus-ring-offset: var(--focus-ring-offset); --toast-action-focus-ring-offset: var(--focus-ring-offset);
--toast-action-focus-ring-width: var(--focus-ring-width); --toast-action-focus-ring-width: var(--focus-ring-width);
--toast-action-focus-ring-color: var(--focus-ring-color); --toast-action-focus-ring-color: var(--focus-ring-color);
--toast-close-size: 28px;
--toast-close-border-width: var(--border-width);
--toast-close-radius: var(--radius-full);
--toast-close-bg: transparent;
--toast-close-border: transparent;
--toast-close-color: var(--color-content-secondary);
--toast-close-hover-bg: var(--color-surface-raised);
--toast-close-hover-border: var(--color-border-subtle);
--toast-close-hover-color: var(--color-content-primary);
--toast-close-focus-ring-offset: var(--focus-ring-offset);
--toast-close-focus-ring-width: var(--focus-ring-width);
--toast-close-focus-ring-color: var(--focus-ring-color);
--toast-default-surface: color-mix( in srgb, var(--color-surface-overlay) 92%, var(--color-neutral-track) ); --toast-default-surface: color-mix( in srgb, var(--color-surface-overlay) 92%, var(--color-neutral-track) );
--toast-default-border: color-mix( in srgb, var(--color-neutral-border) 72%, var(--color-border-default) ); --toast-default-border: color-mix( in srgb, var(--color-neutral-border) 72%, var(--color-border-default) );
--toast-default-accent: var(--color-neutral-border); --toast-default-accent: var(--color-neutral-border);

@ -3572,18 +3572,8 @@ export const THEME_BASE_RECIPE_TOKENS = {
'action-focus-ring-offset': 'var(--focus-ring-offset)', 'action-focus-ring-offset': 'var(--focus-ring-offset)',
'action-focus-ring-width': 'var(--focus-ring-width)', 'action-focus-ring-width': 'var(--focus-ring-width)',
'action-focus-ring-color': 'var(--focus-ring-color)', 'action-focus-ring-color': 'var(--focus-ring-color)',
'close-size': '28px', // `close-*` tokens removed: Toast.Close composes the canonical
'close-border-width': 'var(--border-width)', // <IconButton rounded="full">, so its chrome lives in button.css.
'close-radius': 'var(--radius-full)',
'close-bg': 'transparent',
'close-border': 'transparent',
'close-color': 'var(--color-content-secondary)',
'close-hover-bg': 'var(--color-surface-raised)',
'close-hover-border': 'var(--color-border-subtle)',
'close-hover-color': 'var(--color-content-primary)',
'close-focus-ring-offset': 'var(--focus-ring-offset)',
'close-focus-ring-width': 'var(--focus-ring-width)',
'close-focus-ring-color': 'var(--focus-ring-color)',
'default-surface': 'default-surface':
'color-mix( in srgb, var(--color-surface-overlay) 92%, var(--color-neutral-track) )', 'color-mix( in srgb, var(--color-surface-overlay) 92%, var(--color-neutral-track) )',
'default-border': 'default-border':

Loading…
Cancel
Save

Powered by TurnKey Linux.