Toasts, snackbars, and async confirmations
A toast is a one-line announcement that the user didn't ask for. Get it wrong and you trap users; get it right and it's invisible until needed.
A toast is the small message that pops up after you do something — "Saved," "Copied to clipboard," "Couldn't connect, try again." It is, by definition, an announcement the user didn't ask for. That makes it tricky: shout too loud and you interrupt; whisper and the screen reader user never hears it; leave it on screen too long and it covers the next thing the user wants to click.
This lesson is about three decisions: which live region role, how long the toast lives, and what happens to keyboard focus while it's up.
Toast, snackbar, banner — what's the difference?
Vocabulary varies, but the distinctions that matter for accessibility:
- Toast / snackbar — short, transient, dismisses on its own. "Saved." No focus change.
- Banner / inline alert — persistent until dismissed. "You're offline." Sits in the layout.
- Alert dialog — modal, demands a response. "Save before quitting?" Focus moves into it.
Whatever your design system calls them, the question is: does the user have to act, or is this just a heads-up?
- Heads-up → toast (transient)
- Should-act → banner (persistent)
- Must-act → alert dialog (modal, focus moves)
role=status vs role=alert
The toast itself is a live region. Pick the politeness based on urgency.
role="status" (polite). For successful saves, copies, sent messages. "Email sent." Screen reader announces it when idle.
<div role="status">Email sent.</div>
role="alert" (assertive). For errors, failed saves, network problems. "Couldn't save — try again." Interrupts whatever the screen reader was reading.
<div role="alert">Couldn't save — check your connection.</div>
A bad pattern: using role="alert" for happy-path success messages. "Saved!" doesn't deserve to interrupt anything. Reserve assertive for "this needs your attention now."
Don't pre-render an empty role="status" or role="alert" at page load and then populate it. Some screen readers cache the empty state and never re-announce. Render the toast fresh each time.
How long the toast stays
The default of 3–5 seconds is a sighted-user convention. For accessibility, it's at best a starting point and at worst a problem:
- A screen reader reading a long announcement still hasn't reached your toast when it disappears.
- A user with motor difficulties hasn't located the dismiss button before it auto-dismisses.
- A user with cognitive disabilities hasn't finished reading.
WCAG 2.2.3 (Timing Adjustable) and 2.2.6 (Timeouts) ask you to give users control or enough time. The practical answer:
- Make the duration long enough — 6+ seconds for a short message, longer for longer text.
- Pause on hover and on focus. If the user is engaging with the toast, don't snatch it away.
- Always include a manual close button. The auto-dismiss is a convenience, not the only path out.
function showToast(text) {
const toast = makeToast(text);
document.body.appendChild(toast);
let timer = setTimeout(dismiss, 6000);
toast.addEventListener("mouseenter", () => clearTimeout(timer));
toast.addEventListener("focusin", () => clearTimeout(timer));
toast.addEventListener("mouseleave", () => (timer = setTimeout(dismiss, 6000)));
toast.addEventListener("focusout", () => (timer = setTimeout(dismiss, 6000)));
function dismiss() { toast.remove(); }
}Don't trap the user
A toast must not steal focus. The user is mid-task. If you .focus() the toast or its dismiss button, you've yanked them out of whatever they were doing.
The rule: announce, don't activate. The live region handles the announcement; if the user wants to interact with the toast, they Tab into it. Some toasts include a Dismiss button or an Undo action — fine, they sit in the toast and the user can Tab to them, but you don't move focus there.
Quick test: open your app, focus the search bar, trigger a toast. If your cursor is no longer in the search bar, you broke the contract.
Toasts with actions
Some toasts offer an action — "Undo," "Reconnect," "View". That action is a button inside the toast.
<div role="status"> <span>Message archived.</span> <button type="button">Undo</button> <button type="button" aria-label="Dismiss notification">×</button> </div>
A few rules for action toasts:
- The action must be reachable by keyboard. Don't make it click-only.
- The action must persist long enough to use. Auto-dismiss should be longer (10s+) when an action is offered, or pause on focus.
- The dismiss button needs an accessible name. "×" alone is meaningless to screen readers; use
aria-label="Dismiss notification". - If the action requires immediate attention ("Undo within 5 seconds"), reconsider the design. Time-limited actions are an accessibility trap.
For genuinely critical confirmations — "You're about to delete 47 items. Confirm?" — don't use a toast at all. Use an alert dialog (Lesson 6). Toasts are for "something happened." Dialogs are for "something is about to happen and we need you to weigh in."
Try it yourself
×. What's missing?