Skip to content

Toast

A brief, stacked notification about something that just happened, with an optional action.

Preview

Installation

pnpm add @syntara/react @syntara/tokens
import { 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.

Archived: 0

Dismiss by key

A persistent "working" toast replaced by a result.

Accessibility

KeysAction
F6orShiftF6Moves focus into and out of the notifications region (a landmark).
TaborShiftTabMoves between toasts and their action and close buttons.
EnterorSpaceActivates 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

contentRequired
string | { 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) => void

toast.dismiss(key) closes one toast; toast.dismiss() closes all.

ToastRegion

placement
  • top-start
  • top
  • top-end
  • bottom-start
  • bottom
  • bottom-enddefault

Viewport corner or edge the stack grows from (logical: end = right in LTR, left in RTL).

aria-label
string

Name 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