Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .specs/toast.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ setup: "One-time app-level wiring before the first toast() call — service (rec
figma:
url: https://www.figma.com/design/t97pXRs7xME3SJDs5iZ5RF/Webkit?node-id=478-938
node_id: 478:938
checksum: 27f692b3c2a3b73dcd3e528cbf69ed98e063a057e282bf50a994137557450efc
checksum: 73cc405c1a70f571a239ccca39e40583c18424507ce6581b0d21372807080c9e
created: 2026-06-23
last_updated: 2026-07-21
last_updated: 2026-08-14
---

# Toast — Component Spec
Expand Down Expand Up @@ -112,7 +112,7 @@ import Toast from '@aziontech/webkit/toast'
is presence in the stack, expressed via `data-type` and enter/leave transitions. -->

- `toaster/toaster.vue` — the mount-once region. `<Teleport to="body">` + a fixed-positioned, `position`-anchored container that renders the live `<TransitionGroup>` stack of `ToastItem`s from the injected store, with `aria-live` (`polite`, or `assertive` when an `error` toast is present) and `aria-atomic="false"`. Props `position?: ToastPosition` (default `'bottom-right'`), `duration?: number` (default `4000`, ms — the stack-wide default each toast inherits unless its own `duration` overrides), `max?: number` (cap the number of simultaneously visible toasts; older ones drop when exceeded), `expand?: boolean` (when `false`, the default, stacked toasts overlap into a peek; when `true`, they lay out fully expanded with a gap), `closable?: boolean` (default `false`; when `true`, every toast shows an always-visible close control unless its own `closable` option overrides). Exposes a scoped `default` slot (`{ toast, dismiss }`) for a bespoke body; falls back to composing `ToastItem` from the store entry. This is the public root attached to the compound as `Toast.Toaster`.
- `toast-item/toast-item.vue` — a single notification card (flex row, `items-center` so a single-line toast and its `trailing` action/close share one centered line; the typed leading icon takes `self-start` so on a multi-line toast it rises to the title's first line instead of centering on the block, while the `loading` `Spinner` stays centered; height adapts to content). All typography and spacing come from tokens (`text-label-md` / `text-body-xs`, `var(--spacing-*)`) — no raw sizes or line-heights. An optional leading severity icon — a PrimeIcons glyph by `type` sized with the `text-label-md` token to match the title (`pi pi-check` / `pi pi-info-circle` / `pi pi-exclamation-triangle` / `pi pi-exclamation-circle`); the neutral `default` type has **no** icon, and `loading` renders a `Spinner`. Followed by a content column (`Title` + optional `Description`) in the `default` slot, and a right-aligned `trailing` slot for the optional `Action` and the `Close` control. `role="status"` (or `role="alert"` for `error`/`warning`). Props `type?: ToastType` (default `'default'`). Slots: `default` (title/description) and `trailing` (action/close); the `Toaster` fills both, standalone it renders just the shell. Reflects `data-type`.
- `toast-item/toast-item.vue` — a single notification card (outer flex row, `items-center` so the content and its `trailing` action/close share one centered line; the leading icon and the content column sit in an inner `items-start` group so the typed icon aligns to the title's line — its single line on a one-line toast, the first line on a multi-line one — instead of to the outer row, which the 28px action/close control would otherwise stretch; the `loading` `Spinner` keeps `self-center`. The `trailing` region carries a negative block margin (`-my-(--spacing-xxs)`) so that 28px control does not inflate a one-line card, and a negative inline-end margin (`-mr-(--spacing-xs)`) so the text action's label ends optically flush with the card's padding instead of inset by the button's own padding; height adapts to content). All typography and spacing come from tokens (`text-label-md` / `text-body-xs`, `var(--spacing-*)`) — no raw sizes or line-heights. An optional leading severity icon — a PrimeIcons glyph by `type` sized with the `text-label-md` token to match the title (`pi pi-check` / `pi pi-info-circle` / `pi pi-exclamation-triangle` / `pi pi-exclamation-circle`); the neutral `default` type has **no** icon, and `loading` renders a `Spinner`. Followed by a content column (`Title` + optional `Description`) in the `default` slot, and a right-aligned `trailing` slot for the optional `Action` and the `Close` control. `role="status"` (or `role="alert"` for `error`/`warning`). Props `type?: ToastType` (default `'default'`). Slots: `default` (title/description) and `trailing` (action/close); the `Toaster` fills both, standalone it renders just the shell. Reflects `data-type`.
- `toast-title/toast-title.vue` — the primary line of a toast (`text-body-xs` `var(--text-default)`). Renders its default slot as the title text; standalone markup + testId justify the part.
- `toast-description/toast-description.vue` — the supporting line below the title (`text-body-xs` `var(--text-muted)`). Renders its default slot; standalone markup + testId justify the part.
- `toast-action/toast-action.vue` — the inline action control, rendered as the webkit `Button` (`kind="text"`, `size="small"`). Prop `label: string`. Emits `click: [event: MouseEvent]`. Justified because it owns the button wiring + a11y for the action affordance.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,25 @@
:data-type="type"
class="group pointer-events-auto relative flex w-full items-center gap-(--spacing-sm) rounded-(--shape-elements) border-solid border-[length:var(--border-width-default,1px)] border-(--border-default) bg-(--bg-surface-raised) p-(--spacing-sm) text-(--text-default) shadow-(--shadow-sm)"
>
<Spinner
v-if="type === 'loading'"
class="size-4 shrink-0 text-(--text-muted)"
/>
<i
v-else-if="icon"
:class="icon"
:data-type="type"
class="shrink-0 self-start text-label-md leading-none text-(--text-muted) data-[type=success]:text-(--success-contrast) data-[type=info]:text-(--info-contrast) data-[type=warning]:text-(--warning-contrast) data-[type=error]:text-(--danger-contrast)"
aria-hidden="true"
/>
<div class="flex min-w-0 flex-1 flex-col gap-(--spacing-xxs)">
<slot />
<div class="flex min-w-0 flex-1 items-start gap-(--spacing-sm)">
<Spinner
v-if="type === 'loading'"
class="size-4 shrink-0 self-center text-(--text-muted)"
/>
<i
v-else-if="icon"
:class="icon"
:data-type="type"
class="shrink-0 text-label-md leading-none text-(--text-muted) data-[type=success]:text-(--success-contrast) data-[type=info]:text-(--info-contrast) data-[type=warning]:text-(--warning-contrast) data-[type=error]:text-(--danger-contrast)"
aria-hidden="true"
/>
<div class="flex min-w-0 flex-1 flex-col gap-(--spacing-xxs)">
<slot />
</div>
</div>
<div
v-if="$slots['trailing']"
class="flex shrink-0 items-center gap-(--spacing-xxs)"
class="-my-(--spacing-xxs) -mr-(--spacing-xs) flex shrink-0 items-center gap-(--spacing-xxs)"
>
<slot name="trailing" />
</div>
Expand Down
Loading