Toast
A brief, stacked notification about something that just happened, with an optional action.
Preview
Installation
pnpm add @syntara/react @syntara/tokensimport { ToastRegion, toast } from '@syntara/react';Usage
import { Button, ThemeScope, ToastRegion, toast } from '@syntara/react';
// Mount once, inside your ThemeScope (e.g. in the root layout).
export function App({ children }: { children: React.ReactNode }) {
return (
<ThemeScope theme="vela">
{children}
<ToastRegion />
</ThemeScope>
);
}
export function SaveButton() {
return <Button onPress={() => toast({ title: 'Changes saved', tone: 'success' })}>Save</Button>;
}Examples
Action weight by severity
The reference pattern: a quiet outline action on a success toast, a high-contrast action on an error toast.
With an action
An Undo action; toasts with actions stay until dismissed.
Dismiss by key
A persistent "working" toast replaced by a result.
Accessibility
| Keys | Action |
|---|---|
| F6orShiftF6 | Moves focus into and out of the notifications region (a landmark). |
| TaborShiftTab | Moves between toasts and their action and close buttons. |
| EnterorSpace | Activates the focused action or close button. |
- Each toast is role="alertdialog" (non-modal) with its title and description wired, and its content in a role="alert" live region so it is announced when it appears.
- Timers pause while the pointer is over the region or focus is inside it. When a focused toast closes, focus moves to the next toast, or back to where it was before entering the region.
- Toasts with an action do not auto-dismiss, so keyboard and screen reader users have time to reach it (WCAG 2.2.1).
- At most three toasts are visible; newer ones queue older ones.
- Built on React Aria's UNSTABLE Toast APIs; the maturity stays alpha until those are stable.
- Surface recipe: an opaque surface.raised face under the engine's --syntara-sheen (dark only). Only text.default and text.subtle sit on it; the engine proves text.subtle ≥ 4.5:1 at the sheen's brightest pixel.
- Each tone has its own filled shape (info circle, success seal, warning circled !, danger triangle), and the title says it in words too, so colour is never the only signal. The shape is feedback.<tone>.fg (≥ 6.09:1 against the toast face, WCAG 1.4.11 needs 3:1) with the glyph knocked out in feedback.<tone>.bg (≥ 5.43:1 against the shape), across every tenant and the 1,000 fuzz brands in both schemes. test/toast.test.tsx re-proves it.
- The close button is a 24×24px corner button, always in the tab order and named "Close" (localised). It shows when you hover that toast or focus anything in it, and it's always visible on the front toast on touch screens.
- Toasts slide in from the edge and grow from 0.94 on the spring, and the status shape pops as they land. With reduced motion, toasts only fade in and close at once; nothing slides or springs.
Guidelines
Do
- Mount exactly one ToastRegion, inside your ThemeScope. The region portals to <body> and copies the scope's theme, scheme, density, lang and dir onto itself so it looks and reads like the page. (Extra regions render nothing.)
- Keep titles short and past tense: "Claim submitted".
- Use a toast for results of the user's own actions; offer Undo instead of a confirmation dialog where you can.
- Match the action's weight to the tone: toast() does it for you (contrast for danger/warning, outline otherwise), so only pass a short label.
Don’t
- Don't put information the user must act on only in a toast — use an Alert or Dialog.
- Don't fire several toasts for one action.
- Don't set a timeout below 5 seconds.
- Don't put brand or feedback-coloured text on the toast surface; only text.default and text.subtle are proven on the sheen.
API reference
toast
contentRequiredstring | { title: ReactNode; description?: ReactNode; tone?: 'neutral' | 'info' | 'success' | 'warning' | 'danger'; icon?: ReactNode; action?: { label: string; onAction: () => void } }What to show. A string is the title. Every tone gets a filled status shape (neutral: the info shape in a quiet grey); `icon` replaces it with any icon, coloured by the tone. Pressing the action runs onAction and closes the toast; its weight follows the tone (Button variant="contrast" for danger and warning, "outline" otherwise).
options{ timeout?: number | null; onClose?: () => void }timeout in ms: default 5000, or none when there is an action; null keeps the toast until dismissed. Returns the toast key.
dismiss(key?: string) => voidtoast.dismiss(key) closes one toast; toast.dismiss() closes all.
ToastRegion
placementtop-starttoptop-endbottom-startbottombottom-enddefault
Viewport corner or edge the stack grows from (logical: end = right in LTR, left in RTL).
aria-labelstringName of the notifications landmark.
Default
'Notifications' (localised)
Tokens
The semantic tokens this component reads, grouped by what they control. Swatches show this site’s theme; change a tenant’s brand and the component follows with no code change.
- Colour10
border.defaultfeedback.*.bgfeedback.*.fgfocus.ringsurface.raisedsurface.sunkentext.defaulttext.subtleborder.subtlesurface.default- Type8
font.bodyfont.size.mdfont.size.smfont.weight.semiboldline-height.normalline-height.snugfont.tracking.mdfont.tracking.sm- Space and size8
space.3space.4space.6space.16card-insetspace.5space.1space.2- Shape2
radius.containerradius.pill- Depth3
shadow.overlayshadow.raisedhairline- Motion7
motion.duration.fastmotion.duration.normalmotion.duration.slowmotion.duration.springmotion.easingmotion.easing-outmotion.spring- Other2
sheenrim