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']],
['tags-input/tags-input.svelte', ['data-size', 'data-variant', 'data-color']],
['toolbar/toolbar.svelte', ['data-size', 'data-variant']],
['toast/toast-close.svelte', ['data-icon-only']],
['toast/toast-viewport.svelte', ['data-position']],
['toggle/toggle.svelte', ['data-size', 'data-variant', 'data-block', 'data-icon-only']],
['tooltip/tooltip-content.svelte', ['data-size', 'data-variant']],

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

@ -59,6 +59,10 @@ export type BannerProps = Omit<HTMLAttributes<HTMLElement>, 'children'> & {
export type BannerCloseProps = Omit<IconButtonProps, 'aria-label' | 'children'> & {
/** Accessible label for the dismiss button. @default localized 'Dismiss' */
'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;
};

@ -1,27 +1,41 @@
<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 { IconButton } from '$uix/eidos/components/icon-button';
import { SvgX } from '$uix/eidos/components/svg';
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>
<Toast.Close {...rest} data-icon-only={!children ? '' : undefined}>
{#if children}
{@render children?.()}
{:else}
<svg
viewBox="0 0 24 24"
width="14"
height="14"
fill="none"
stroke="currentColor"
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 {...rest}>
{#snippet child({ props })}
{#if outerChild}
{@render outerChild({ props })}
{:else}
<IconButton {...props} {variant} size={resolvedSize} {color} rounded="full" aria-label={closeLabel}>
{#if children}{@render children()}{:else}<SvgX />{/if}
</IconButton>
{/if}
{/snippet}
</Toast.Close>

@ -299,36 +299,6 @@
var(--toast-action-focus-ring-color);
}
[data-toast-close] {
display: inline-flex;
align-items: center;
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);
}
/* `Toast.Close` composes the canonical `<IconButton rounded="full">`, so its
chrome (size, bg, border, hover, focus ring) comes from button.css. The toast
item grid (above) places it in the trailing `auto` column. */

@ -12,6 +12,8 @@ import type {
ToastData,
Toaster
} 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
@ -30,7 +32,14 @@ export type ToastProps = ProviderProps;
export type ToastTitleProps = TitleProps;
export type ToastDescriptionProps = DescriptionProps;
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 ToastStatusProps = StatusProps;
export type ToastMainProps = MainProps;

@ -3070,18 +3070,6 @@
--toast-action-focus-ring-offset: var(--focus-ring-offset);
--toast-action-focus-ring-width: var(--focus-ring-width);
--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-border: color-mix( in srgb, var(--color-neutral-border) 72%, var(--color-border-default) );
--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-width': 'var(--focus-ring-width)',
'action-focus-ring-color': 'var(--focus-ring-color)',
'close-size': '28px',
'close-border-width': 'var(--border-width)',
'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)',
// `close-*` tokens removed: Toast.Close composes the canonical
// <IconButton rounded="full">, so its chrome lives in button.css.
'default-surface':
'color-mix( in srgb, var(--color-surface-overlay) 92%, var(--color-neutral-track) )',
'default-border':

Loading…
Cancel
Save

Powered by TurnKey Linux.