Toast
ImplementedEphemeral notification card — 4 variants (success/info/warning/error). Card-only: positioning, stacking and dismissal timing belong to the consuming container (no DS container molecule yet). error announces via role='alert' + aria-live='assertive'; the other variants via role='status' + aria-live='polite'.
patterns/molecules/toast.htmlEphemeral notification card — 4 variants (success/info/warning/error). Card-only: the molecule announces itself and fires onDismiss(id); positioning, stacking, and dismissal timing belong to the consuming app (a DS-side container is not shipped yet).
Toast
Feedback molecule — Family 4
A single toast card: icon, title, optional description, optional inline action, dismiss button. Presentation-only — it never unmounts itself and has no timer. The consuming app owns the overlay position, the stack, and removal. The ToastMolecule export is a plain alias of this component; there is no separate molecule.
This component supports light and dark mode.
Usage
A Toast is a self-announcing message card. It has no position, no stack, and no timer — mount it inside your own overlay layer and remove it when onDismiss(id) fires. id, variant, and title are required with no defaults — a bare <Toast /> crashes at render.
import { Toast } from '@epasslive/design-system'
<Toast
id="evt-2026-saved"
variant="success"
title="Changes saved"
description="Your event details were updated."
onDismiss={removeToast}
/>
<Toast
id="pay-retry"
variant="error"
title="Payment failed"
description="Your card was declined."
action={{ label: 'Retry', onClick: retryPayment }}
onDismiss={removeToast}
/>The barrel also exports ToastMolecule — a plain alias of Toast, both pointing at the same module. Use whichever name matches your import style; the alias never diverges.
Alert vs. Toast vs. Notification
Three feedback surfaces overlap; pick by persistence and who owns the announcement. Rule of thumb: in-flow status → Alert, transient overlay → Toast, durable per-user feed item → Notification.
If the message must persist in the layout (no overlay), it is an Alert or a Notification — not a Toast. If you only need the unread count on the bell trigger, that is a Badge.
Container contract (not yet shipped)
The molecule ships as a card only — there is no ToastContainer, no queue, and no auto-dismiss anywhere in the DS today. Your app owns the overlay: render toasts in a fixed viewport region and remove them on onDismiss(id). The card already applies pointer-events-auto, expecting a pointer-events-none viewport parent.
When a DS-side container lands, it must satisfy at minimum: layer z-ep-toast (70, above popovers — currently misused by Tooltip; fix scheduled); auto-dismiss default ~5s with pause on hover/focus (WCAG 2.2.1); Escape dismisses the focused toast; focus return to a sensible target after dismiss; and forwarding of the (today inert) duration prop.
Accessibility
Announcement is variant-driven, per the WAI-ARIA APG: variant="error" renders role="alert" + aria-live="assertive" so screen readers interrupt current output; every other variant renders role="status" + aria-live="polite" and waits. aria-atomic="true" is always set.
The variant icon is aria-hidden — the visible title and description carry the message. The dismiss button is a native <button> labeled via dismissLabel (English default "Dismiss notification" — pass an explicit label on localized pages) with a teal focus-visible ring. The root mirrors data-toast-id and data-toast-variant for end-to-end tests.
Known gap (scheduled fix): the dismiss (28×28px) and action buttons sit under the 44px --ep-tap-min canonical tap target.
Dark mode
The card uses bg-ep-surface-card, ring-ep-overlay-border-subtle, and shadow-ep-md — all flip automatically with the theme. Left-border accents and icons use semantic tokens (ep-semantic-success / ep-semantic-warning / ep-semantic-error; info uses ep-teal + ep-text-teal) with dark overrides, so contrast holds in both themes. Icons are Phosphor SSR glyphs colored per variant by token classes.
Props
id*stringStable identifier for this toast. Forwards to data-toast-id on the root — the consuming app targets dismissal and updates by it.
variant*ToastVariantSemantic tone: "success" | "info" | "warning" | "error". Drives the left-border accent, the icon glyph, and the announcement role (error differs — see Accessibility).
title*stringHeadline text. Read first by assistive tech.
descriptionstringOptional supporting copy rendered under the title.
action{ label: string; onClick: () => void }Optional inline action — teal text button under the message. Clicking it calls onClick and then onDismiss(id); the app owns the actual removal.
durationnumberDeclared but not currently forwarded to anything (no timer exists anywhere in the DS). Reserved for the future container contract — do not rely on it yet.
onDismiss(id: string) => voidCalled with the toast id when the dismiss button (or an action button) is clicked. The molecule never unmounts itself — the app removes the toast.
dismissLabelstringAccessible label for the dismiss button. Defaults to the English "Dismiss notification" — pass an explicit label on non-English pages.
classNamestringAdditional classes appended to the toast root.
Props
Toast
idstring—Forwards to data-toast-id — unique per toast so a container can target dismiss/update by id.
variant'success' | 'info' | 'warning' | 'error'—Visual and announcement variant; error switches the card to role='alert' / aria-live='assertive'.
titlestring—Headline text of the card.
descriptionstring—Secondary body copy under the title.
action{ label: string; onClick: () => void }—Inline text action (e.g. Retry). onClick carries no payload — close the toast via onDismiss.
durationnumber—Reserved for the future container contract — declared on the interface but currently never read or forwarded by the card.
onDismiss(id: string) => void—Renders the dismiss button; invoked with the toast id.
dismissLabelstring'Dismiss notification'aria-label for the dismiss button (EN default — pass a localized string on non-EN pages).
classNamestring—Additional CSS classes on the card.
<Toast />Accessibility
Type
1 ruleARIA
1 ruleUsage
Import
import { Toast } from "@epasslive/design-system"Example 1
<Toast id="save-ok" variant="success" title="Changes saved" />Example 2
<Toast id="pay-fail" variant="error" title="Payment failed" description="Card declined." action={{ label: "Retry", onClick: retry }} onDismiss={dismiss} />Anti-patterns
— Do not promise auto-dismiss — the card ships no timer and duration is not yet forwarded
— Do not force every variant into one polite live region — error must stay alert/assertive