Designer view
Toast
A transient floating notification anchored to a corner of the viewport, auto-dismissing after a short visible duration. Distinct from Alert (inline, persistent) by its ephemerality and positioning; distinct from Modal (blocking) by its non-modal contract. Used for success confirmations after asynchronous actions, transient status messages, undoable operations, and short notifications that do not require explicit acknowledgement.
When to use
Use
For transient confirmations after asynchronous actions ("Saved", "Copied"), undoable operations ("Item deleted · Undo"), and short non-essential status updates the user can safely miss. Toast is non-blocking and auto-dismisses; users do not need to acknowledge.
Avoid
For critical errors requiring user action — that is `Alert` (inline persistent) or `Modal` (blocking). For long informational content — that is `Alert` or page prose. For on-demand description — that is `Tooltip`. For page-wide persistent notices — that is `Banner`.
Versus related
- alert
`Alert` is inline and persistent; `Toast` is floating and auto-dismissed. Alert is for situations the user may need to revisit or act on; Toast is for transient signals the user can miss without consequence.
- modal
`Modal[variant=alertdialog]` is blocking and demands explicit response; `Toast` is non-blocking and ephemeral. They occupy opposite ends of the notification-urgency spectrum.
- banner
`Banner` is page-wide, persistent, and informational (e.g. a maintenance notice at the top of every page); `Toast` is corner-anchored, ephemeral, and action-triggered. Banner is system-level; Toast is action-level.
- tooltip
`Tooltip` is hover/focus-revealed descriptive text; `Toast` is system-pushed notification. Tooltip is pulled by the user; Toast is pushed by the system.
Figma anatomy
| Slot | Figma type | Hint |
|---|---|---|
viewport | frame | Fixed-position frame at one viewport corner; padding from safe-area-inset |
container | frame | Auto-layout horizontal frame; severity drives accent treatment |
icon | instance | Icon component instance; glyph swap bound to severity Variant |
title | text | Heading text style; bound to a component property |
body | text | Body text style; bound to a component property |
action | instance | Button instance; visibility bound to hasAction property |
dismiss-button | instance | Icon button instance; visibility bound to dismissible property |
Token usage per slot
viewport- spacing
- padding
spacing.compact - gap
spacing.tight
- padding
container- spacing
- padding
spacing.compact - gap
spacing.compact
- padding
- radius
- corner
radius.md
- corner
- color
- background
color.surface.raised - border
color.border.subtle
- background
- elevation
- shadow
elevation.lg
- shadow
icon- color
- foreground
color.text.accent
- foreground
title- color
- foreground
color.text.primary
- foreground
- typography
- size
text.sm - weight
weight.semibold
- size
body- color
- foreground
color.text.primary
- foreground
- typography
- size
text.sm - lineHeight
leading.snug
- size
action- spacing
- padding
spacing.tight
- padding
- radius
- corner
radius.sm
- corner
- color
- foreground
color.text.accent - ring
color.border.focus
- foreground
dismiss-button- spacing
- padding
spacing.tight
- padding
- radius
- corner
radius.sm
- corner
- color
- foreground
color.text.muted - ring
color.border.focus
- foreground
Figma ↔ Code property map
| Figma | Type | Code | Notes |
|---|---|---|---|
Severity | Variant | severity | Maps info / success / warning / error. Drives role (status vs alert) plus visual treatment plus icon glyph. |
Position | Variant | position | Six options covering corners and centres. Set on the viewport, not per-toast. |
Dismissible | Boolean | dismissible | Toggles the dismiss-button slot visibility plus Escape handling. |
Has Icon | Boolean | hasIcon | — |
Has Title | Boolean | hasTitle | — |
Has Action | Boolean | hasAction | Toggles the action slot. Single action per toast by canon. |
Title | Text | title | — |
Body | Text | body | — |
Action Label | Text | actionLabel | — |
Icon | Instance Swap | icon | Severity icon glyph; designers may swap for domain-specific icons. |
Motion
| Transition | Duration token |
|---|---|
open | motion.duration.fast |
close | motion.duration.fast |
swipeDismiss | motion.duration.fast |
Responsive behaviour
| Breakpoint | Change |
|---|---|
breakpoint.sm | At and below, the toast viewport spans the inline-size of the screen and anchors to the bottom regardless of the authored `position` (top-anchored toasts on mobile cover the address bar or status area). Toast container fills the inline-size minus safe-area padding. Stack depth canonical maximum drops from 3 to 2 to preserve screen real estate. |
breakpoint.md | Above this width, the viewport renders at the authored position with toasts sized to their max-inline-size. Stack depth canonical maximum is 3. |
Internationalisation
RTL · mirroring
Position values are logical: `start` and `end` follow inline direction (top-end is visual top-right in LTR, top-left in RTL). Dismiss button moves with the inline-end logical anchor. Action button order is unchanged within the toast (single action by canon). Severity icons are direction-neutral. Slide-in animations use logical transform directions — toasts entering from inline-end slide from visual right in LTR, visual left in RTL.
Text expansion
Title and body grow with translation. Max-inline-size on the container constrains horizontal growth; long-text languages wrap to additional lines. Auto-dismiss duration may need to extend in long-text locales — the canonical formula is "2 seconds + 1 second per ~50 characters" rather than a fixed duration. Action button label follows Button's expansion rules; single-action toasts do not have multi-button wrap issues.
Variants, properties, states
Variants
Structurally different versions of the component.
infosuccesswarningerror Properties
The same component, parameterised.
| Property | Type |
|---|---|
position | top-start | top-end | bottom-start | bottom-end | top-center | bottom-center |
dismissible | boolean |
hasAction | boolean |
hasIcon | boolean |
States
Browser/user-driven (interactive) vs. app-driven (data).
| Kind | States |
|---|---|
interactive | hoverfocus-visible |
data | openingopenpausedclosingclosed |
State transitions
| From | To | Trigger |
|---|---|---|
closed | opening | Consumer triggers the toast (typically after an async operation completes). The toast is mounted in the viewport region; the auto-dismiss timer is initialised but not yet started. |
opening | open | The slide-in animation completes (or, under prefers-reduced-motion reduce, immediately). The auto-dismiss timer starts; the live region announces the toast content. |
open | paused | User hovers anywhere inside the toast viewport region OR focus moves into the toast region. The auto-dismiss timer pauses for all toasts in the viewport, not just the hovered one. |
paused | open | User leaves the viewport region (pointer and focus both out). The auto-dismiss timer resumes from where it paused (NOT restarted from full duration — the elapsed time before pause counts). |
open | closing | The auto-dismiss timer expires; or the user presses Escape with focus inside the toast region; or the user activates the dismiss button or invokes the action; or the consumer programmatically closes the toast. |
closing | closed | The slide-out animation completes (or immediately under reduced motion). The toast is removed from the viewport and from the live region. Subsequent toasts in the stack reflow. |
Figma↔Code mismatches
- 01 Figma
A toast drawn next to the trigger that surfaced it
CodeA toast rendered in a fixed-position viewport region at a corner of the screen
ConsequenceDesigners may anchor toasts to specific UI elements in mocks (next to the save button, below the form). Implementations following the Figma file create per-trigger floating surfaces that compete with each other and miss the stacking, the auto-dismiss, and the live-region semantics.
CorrectToast lives in a single viewport region per page (or per window scope), not anchored to triggers. The position property selects which corner; the viewport region handles stacking and live-region semantics. Document the singleton viewport contract explicitly.
- 02 Figma
Toast auto-dismiss drawn as a single visual state with no progress indicator
CodeAn auto-dismiss timer driving slide-out at a configurable duration
ConsequenceDesigners may not realise the toast disappears automatically; developers ship without a progress affordance and users feel surprised when the toast vanishes mid-read. Worse, implementations may default to too-short or too-long durations.
CorrectDocument auto-dismiss as a canonical behaviour with a configurable duration property (or a fixed canonical default such as 5 seconds). An optional progress indicator slot may visualise remaining time. Hover-pause is canonical to protect users who are reading.
- 03 Figma
Multiple toast variants for stacking visualisation
CodeA single toast component plus a viewport region that handles stacking
ConsequenceDesigners create "single toast", "two toasts", "three toasts" variants in the design file. Developers ship one toast plus stacking logic; the design file's three-toast variant is decorative, not real, and the production stack behaves differently.
CorrectStacking is a viewport-region concern, not a toast variant. Document the canonical stack-depth maximum and the stacking animation in the viewport's anatomy slot. The toast itself is a single component; the viewport orchestrates.
- 04 Figma
Toast action drawn as link-styled inline text inside body
CodeToast action as a Button in the action slot
ConsequenceDesigners compose body text with inline link "Undo"; the embedded interaction is either a real anchor (wrong semantic — Undo does not navigate) or a span with click handler (no semantic at all). Keyboard reachability and announcement contracts both suffer.
CorrectSingle action lives in the dedicated action slot as a Button element (typically with `variant: tertiary` or `ghost`). The body describes the situation; the action is separately reachable by Tab.
Common mistakes
#toast-no-pause-on-hover
Auto-dismiss continues while the user reads
The toast slides in, the user starts reading, the timer fires, the toast slides out. Users with motor impairments, cognitive impairments, or simply slow reading speed cannot reliably consume the message. WCAG 2.2.1 (Timing Adjustable) is failed.
Pause the auto-dismiss timer on pointer-enter and focus-into the viewport region. Resume on pointer-leave AND focus-out (both must be true). Some implementations pause on individual toast hover; canonical pauses on viewport-level hover so users can move between adjacent toasts.
#toast-stack-overflow
Toast stack grows unbounded
Multiple toasts fire in rapid succession; the stack grows to 5, 10, 20 toasts. SR users hear a torrent of announcements; pointer users see the screen filled with notifications. The viewport overflows.
Cap the canonical stack depth at 3 visible toasts. Beyond that, queue new toasts and only render them when an existing toast dismisses. Document the threshold in performance. The queue is invisible — users do not know how many toasts are pending.
#toast-essential-info
Critical errors delivered as toast
A network failure or data-loss warning is shown as an auto-dismissing toast. The user misses it (auto-dismiss); the underlying problem persists; data is lost without recourse.
Critical errors that require user action are Alert (inline persistent) or Modal (blocking). Toast is reserved for transient confirmations and non-essential updates the user can miss without consequence. Document the canonical severity-to-component map: success and info → toast or alert; warning → alert; error requiring action → alert or modal.
#toast-aria-live-assertive-for-info
Info-severity toast uses assertive live region
Toasts carry `role="alert"` (assertive) regardless of severity. SR users are interrupted for every "Saved" / "Copied to clipboard" confirmation; the assertive flood makes them disable notifications entirely.
Match severity to live-region politeness: info / success → `role="status"` (polite), error → `role="alert"` (assertive), warning is canonical-default polite (re-evaluate per use case). Document the mapping; reserve assertive for content that genuinely interrupts.
#toast-no-keyboard-dismiss
Escape does not dismiss the focused toast
Keyboard users tabbing into the toast region cannot exit via Escape — the only way to dismiss is to tab to the dismiss button and activate, or wait for auto-dismiss. The keyboard contract feels broken compared to other light-dismiss patterns (Popover, Tooltip).
Bind Escape on the viewport region: when focus is inside the toast region, Escape dismisses the focused toast (or the most recent toast if none is focused). Document the keyboard contract in a11yAcceptance.
Accessibility hints
| Slot | Accessibility hint | |
|---|---|---|
viewport | Apply `role="region"` with `aria-label="Notifications"` so SR users can navigate to the toast region by landmark. The region itself is not a live region — individual toasts inside carry their own polite or assertive live-region roles depending on severity. Hover anywhere inside the viewport pauses the auto-dismiss timers of all visible toasts. | |
container | Apply `role="status"` (polite, default) for info and success severities, or `role="alert"` (assertive) for error severities. Warning sits on the boundary — canonical default is polite to avoid interrupting the user's work. Both roles imply implicit live regions. | |
icon | Decorative — `aria-hidden="true"`. Severity is communicated through the `role` and the visible body text, never through the icon glyph alone. | |
title | Plain `<strong>` or styled `<p>`; heading semantics not required for transient toasts. SR announces the title followed by the body in DOM order. | |
body | Plain prose. Capped at one to two sentences by canon. Anything longer should be an Alert. | |
action | Real button with accessible name. Receives focus when the user tabs into the toast region (after the dismiss button); pressing Tab exits the region. Activation triggers the consumer-provided handler and typically dismisses the toast as a side effect. | |
dismiss-button | Provide an accessible name ("Dismiss" or "Dismiss notification"). Activation removes the toast from the viewport and from the live region. Escape key (when toast region has focus) does the same. |