Toast
Accessible toast notifications using WAI-ARIA live regions and the native Popover API. info / success / warning / error severity, pause-on-hover, no focus theft, compile-time enforcement of accessible titles.
npx artui@latest add toastPlayground
Toasts render as neutral cards with a colored left accent border per type, and auto-dismiss after 5s. Press Alt+T to move focus into the newest toast, then Esc to dismiss it.
Usage
Shortcut helpers
import { useToast } from '@/components/toast';
function SaveButton() {
const toast = useToast();
return (
<button onClick={() => toast.success('Changes saved.')}>
Save
</button>
);
}Full options via show()
const toast = useToast();
toast.show({
title: 'Profile updated.',
description: 'Your display name is now visible to collaborators.',
type: 'success',
duration: 8000,
});Persistent error with an action
Error toasts with a short duration and no action fire a dev warning. Pass duration: null and provide an action so the user can respond.
toast.error('Upload failed. The file may be corrupted.', {
duration: null,
action: {
label: 'Retry',
onAction: retryUpload,
},
});Awaiting dismissal
ToastHandle.done resolves when the toast is dismissed, expired, collapsed, or the provider unmounts.
const handle = toast.warning('Saving draft…', { duration: null });
await saveDraft();
handle.dismiss();
await handle.done;
showNextStep();API
ToastProvider
Mount exactly one ToastProvider at the root of your application. Mounting more than one duplicates the live-region DOM nodes; a dev overlay fires if duplicates are detected.
| Prop | Type | Default | Description |
|---|---|---|---|
| childrenrequired | ReactNode | none | Application content. Mount exactly one ToastProvider at the root of your app. |
| maxVisible | number | 3 | Maximum number of toasts visible at once per region. Oldest is collapsed when the limit is exceeded. |
| defaultDuration | number | null | 5000 | Default auto-dismiss duration in milliseconds. Pass null for persistent toasts that require explicit dismissal. |
ToastOptions
Passed to toast.show(). The shortcut helpers (toast.success(), toast.error(), …) accept the same fields minus title and type.
| Prop | Type | Default | Description |
|---|---|---|---|
| titlerequired | AccessibleText | none | Required accessible title for the notification. Empty strings and placeholder values are rejected at compile time. |
| description | string | none | Optional secondary body text shown below the title. |
| type | ToastType | "info" | Notification severity. info and success are polite; warning and error are assertive. |
| duration | number | null | none | Per-toast duration override. null makes the toast persistent. Action toasts have a 10 s minimum. |
| action | ToastAction | none | Optional action button. Rendered as a real <button>; invoking it dismisses the toast. |
ToastAction
| Prop | Type | Default | Description |
|---|---|---|---|
| labelrequired | AccessibleText | none | Visible label for the action button. Placeholder values are compile errors. |
| onActionrequired | () => void | none | Called when the action button is activated. |
ToastHandle
Returned by toast.show() and all shortcut helpers.
| Prop | Type | Default | Description |
|---|---|---|---|
| id | string | none | Unique identifier for this toast. |
| dismiss | () => void | none | Programmatically dismiss the toast. |
| done | Promise<void> | none | Resolves when the toast leaves the DOM for any reason. |
useToast()
Returns a ToastApi. Must be called inside a ToastProvider.
| Prop | Type | Default | Description |
|---|---|---|---|
| show | (opts: ToastOptions) => ToastHandle | none | Show a toast with full options. |
| success | (title: AccessibleText, opts?: ToastShortcutOptions) => ToastHandle | none | Shortcut for type: "success". |
| error | (title: AccessibleText, opts?: ToastShortcutOptions) => ToastHandle | none | Shortcut for type: "error". |
| warning | (title: AccessibleText, opts?: ToastShortcutOptions) => ToastHandle | none | Shortcut for type: "warning". |
| info | (title: AccessibleText, opts?: ToastShortcutOptions) => ToastHandle | none | Shortcut for type: "info". |
Keyboard
| Key | Action |
|---|---|
| Alt+T | Move focus into the newest visible toast (action → close → toast) |
| Escape | Dismiss the focused toast and return focus to the element that was active when it was shown |
| Tab | Move focus between the action and close buttons within a toast |
| F6 | Cycle through page landmarks (browser native; reaches the toast region as a landmark) |
| Enter | Activate the focused action or close button |
| Space | Activate the focused action or close button |
Focus is never moved to a toast on render. The timer pauses while a toast has keyboard focus and resumes when focus leaves.
Accessibility
| Criterion | Name | How the component satisfies it |
|---|---|---|
| 4.1.3(AA) | Status Messages | Info and success toasts are rendered in an aria-live=polite region (role=status). Warning and error toasts are rendered in an aria-live=assertive region (role=alert). Both regions are mounted as persistent DOM nodes so screen readers maintain their subscription even after all toasts dismiss. |
| 2.2.1(A) | Timing Adjustable | Timers pause on hover, keyboard focus, document hidden, and prefers-reduced-motion. The 10 s minimum is enforced for action toasts. A dev warning fires for error toasts with short durations. |
| 2.1.1(A) | Keyboard | Esc dismisses the focused toast. Alt+T moves focus to the most-recent toast's action or close button. Action and close are real <button> elements activatable by keyboard. |
| 2.4.3(A) | Focus Order | Toasts do not steal focus on render. Focus returns to the element that was focused when show() was called, when the toast is dismissed via close/action/Esc: unless the user has already moved focus elsewhere. |
| 1.4.1(A) | Use of Color | Type (info/success/warning/error) is conveyed via a visually-hidden text prefix on the title, not color alone. |
| 2.4.7(AA) | Focus Visible | Focus indicators on action and close buttons use outline (not box-shadow) to remain visible in Windows High Contrast Mode. |
Do
Mount exactly one ToastProvider at your app root
// app/layout.tsx
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body>
<ToastProvider>{children}</ToastProvider>
</body>
</html>
);
}Use duration: null for errors and any toast with an action
toast.error('Payment declined. Please check your card details.', {
duration: null,
action: { label: 'Update card', onAction: openBillingSettings },
});Use shortcut helpers for common severity levels
toast.success('Invitation sent.');
toast.warning('Draft autosaved. Manual save recommended before closing.');Await handle.done when you need to chain work after dismissal
const handle = toast.info('Processing…', { duration: null });
await processData();
handle.dismiss();Don't
Mount multiple ToastProvider instances
<ToastProvider>
<Header />
<ToastProvider> {/* dev overlay fires */}
<MainContent />
</ToastProvider>
</ToastProvider>A dev overlay fires and screen readers announce every notification twice.
Pass an empty or placeholder title
toast.show({ title: '' }); // compile error
toast.show({ title: 'title' }); // compile error
toast.show({ title: 'TODO' }); // compile errorAccessibleText rejects these at compile time.
Use error toasts with short auto-dismiss and no action
toast.error('Sync failed.', { duration: 3000 });
// Dev warning: error with duration 3000ms and no action.Pass duration: null or add an action so users can recover.
Call useToast() outside ToastProvider
function Widget() {
const toast = useToast(); // throws if no provider in the tree
}Slider
Accessible single-thumb and range slider following WAI-ARIA APG Slider patterns, with compile-time per-thumb name enforcement, automatic aria-valuetext, and dynamic dependent aria-valuemin/aria-valuemax.
Theme
Customise artui's design tokens (colours, radius, focus ring, shadows) by redefining CSS custom properties after importing artui-tokens.css.