Toast

A line in a corner that says something just happened. It asks for nothing, offers at most one way back, and leaves.

Try it

Rest the pointer on one and its clock stops. Press F6 to move the focus to the newest, and Esc to dismiss it and go back to where you were.

Choosing a toast

A toast is for what has already happened. It appears away from where the reader is looking and it goes on its own, so nothing they cannot do without belongs in it, and its action is never the only way to that action: the undo it offers is also in the page, or the thing can simply be done again.

A toast, or something that stays
WhenUse
Something the reader just did has happened, and they can go onToast
It concerns a part of the page and stays true while they look at itAn inline alert, beside that part
A field is wrongThe Field’s own error, under the field
They must decide before anything elseDialog

Anatomy

Link copiedAppointment deletedUndo
Couldn’t send the inviteThe room is booked until 15:00A new version is ready
Toast geometry, in pixels
PartValue
Least height48
Width288 to 360; the message wraps
Padding16 before, 8 after, 8 block
Radius12
Text, Medium14 / 22
Icon20
Close and action32 tall, radius 8
From the viewport24
Between toasts8

A message, at most one action, and a close that is always there. No title and no second line: what needs those is an alert or a dialog. 3 show at once, the newest nearest the edge, and the rest wait their turn. Under 480 wide every placement becomes the bottom edge, the width of the screen.

The inverse surface, and no colour

The Tooltip sits on the overlay surface, the menu’s, and the toast does not. A tooltip opens where the reader is already looking and can be quiet. A toast opens in a corner they are not looking at and has to be seen: the inverse surface is 15:1 or more against anything it floats over, in both modes.

The tone is read from the shape of the icon, and said in words to a screen reader. It has no colour yet. The theme has text and a border for the inverse surface and nothing else, and the status colours were chosen against light surfaces in light: success green on this near-black does not read. When the theme has status tokens for this surface the icon takes them, and nothing in the API moves.

Hover and pressed are the theme’s own wash, which lightens this surface in light and darkens it in dark, a surface step each way. The focus ring is the text’s colour: in dark this surface is a near-white block, and the focus colour is 1.64:1 on it.

Time

How long a toast stays (WCAG 2.2.1)
ToastStays
A message5s
A message with an action10s
An errorUntil dismissed

The clock stops while the pointer is over the toasts, while the focus is among them, and while the tab is in the background, and starts again from the whole duration. A toast raised again with the id of one that is showing updates it in place: “Uploading” becomes “Uploaded” without a second toast.

Accessibility

The Toaster is a named region that is in the page, empty, from the start, holding a polite live list: a screen reader is already listening when the first toast is put there. An error is also an alert, so it interrupts. F6 moves the focus to the newest toast, Tab reaches its buttons, and Esc dismisses it and gives the focus back.

It lives in the top layer as a manual popover: no z-index and no portal, and it is raised again whenever toasts begin, so it sits above a menu or a dialog opened since. One limit is the platform’s. A modal dialog makes everything outside it inert, the top layer included, so a toast raised while a Dialog is open is drawn above it and can be neither pressed nor heard. Say it inside the Dialog, or close the Dialog first.

Using it

TSX
import { Toaster, toast } from 'alpenglow';

// Once, near the root.
<Toaster />

// From anywhere, a component or not.
const id = toast('Appointment deleted', {
  tone: 'success',
  action: { label: 'Undo', onClick: restore },
});

toast.dismiss(id);
toast(message, options)
OptionTypeDefault
messagestringrequired
tone'neutral' | 'success' | 'danger' | 'warning' | 'info''neutral'
action{ label: string; onClick: () => void }—
durationnumber, or Infinityby tone and action
idstringgenerated
Toaster props
PropTypeDefault
placement'top-start' | 'top-center' | 'top-end' | 'bottom-start' | 'bottom-center' | 'bottom-end''bottom-end'
labelstring'Notifications'
closeLabelstring'Dismiss'
classNamestring—