Feedback
Toast / Status messages
Non-modal, transient messages announced via ARIA live regions.
Last verified against WCAG 2.2 and WAI-ARIA APG 1.2 on 2026-07-02.
Implementation
Persistent live region, auto-dismiss after 4s, manual Dismiss control, non-modal (never steals focus).
toast-pattern.tsx
function ToastPattern() {
const [message, setMessage] = useState(null);
const timeoutRef = useRef(null);
function showToast() {
setMessage("Changes saved successfully.");
timeoutRef.current = setTimeout(() => setMessage(null), 4000);
}
return (
<>
<button onClick={showToast}>Show success toast</button>
{/* This node exists BEFORE any message text does — many screen
readers only announce live-region content if the region was
already present in the tree when the content changes. */}
<div role="status" aria-live="polite" className="sr-only">
{message}
</div>
{message && (
<div className="toast">
<p>{message}</p>
<button onClick={() => setMessage(null)}>Dismiss</button>
</div>
)}
</>
);
}Live demo
Required roles, states & properties
| Element | Attribute | Why |
|---|---|---|
| Live region container | role="status" (implicit aria-live="polite") | Announces routine, non-urgent confirmations without interrupting whatever the user is currently doing or reading. |
| Live region container (urgent variant) | role="alert" (implicit aria-live="assertive") | Reserve for genuinely urgent/error toasts — assertive announcements interrupt the screen reader's current speech, which is disruptive if overused. |
| Live region container | Mounted persistently, content swapped via state | The region must already exist in the DOM before text is injected — mounting the container and its content in the same paint means many AT never "discover" the region in time to announce it. |
| Dismiss control (if present) | aria-label="Dismiss notification" | An icon-only or terse dismiss control needs an explicit accessible name distinguishing it from other Dismiss/Close controls on the page. |
Keyboard interaction model
| Key | Behavior |
|---|---|
| (none — toast never receives focus) | A toast must never steal keyboard focus from whatever the user was doing when it appeared. |
| Tab (to reach an optional Dismiss button) | If a toast includes a dismiss control, it should be reachable in the natural tab order, not force-focused. |
| Enter / Space (on Dismiss) | Dismisses the toast early, before its auto-dismiss timer elapses. |
Focus management rules
- A toast never receives focus on appearance and never traps focus — it is non-modal by definition.
- The live-region container is mounted once, persistently, for the lifetime of the page; only its text content changes.
- If a Dismiss control exists, it sits in the natural tab order near where the toast renders, not force-focused.
- Dismissing (manually or via timeout) does not move focus anywhere — the user's focus was never disturbed in the first place.
WCAG 2.2 success criteria mapping
| SC | Name | Level | Why it applies |
|---|---|---|---|
| 4.1.3 | Status Messages | AA | Status messages can be programmatically determined via role or properties (e.g. aria-live) without receiving focus. |