Patterns
Notifications
Which channel to use for feedback, severity levels and live regions.
A product tells users something every day: a field is wrong, a record is complete, the system is in maintenance. This pattern decides the channel, the severity and the live region for each message.
When to use
Section titled “When to use”- When you report the result of an action, a warning or an error to the user.
- When you decide where a message goes: under a field, in a section, on the page, as a short confirmation or in the notification centre.
- When you choose how a message is announced to a screen reader.
Live example
Section titled “Live example”Try the same message in three channels: InlineAlert for a section, Banner for the whole page, a toast for a short confirmation. The success toast offers “Undo”. Messages that appear later are announced: errors assertively, the rest politely.
import { Banner, Button, InlineAlert, Inline, Stack, Text, useToast } from '@bigbang/ui';import { useState } from 'react';import { type Lang, LangProvider } from '../lang';
const copy = { tr: { intro: 'Aynı iletiyi üç kanaldan deneyin: satır içi uyarı, sayfa bandı ve toast. Ekran okuyucu açıkken dinleyin.', inline: 'Satır içi uyarı (bölüm)', banner: 'Banner (sayfa geneli)', toast: 'Toast (geçici onay)', tones: { success: 'Başarı', warning: 'Uyarı', danger: 'Hata' }, kinds: { success: { title: 'Ayarlar kaydedildi', text: 'Ayarlar kaydedildi.' }, warning: { title: 'Depolama dolmak üzere', text: 'Depolama alanının %80 kadarı dolu.' }, danger: { title: 'Rapor gönderilemedi', text: 'Rapor gönderilemedi. Bağlantınızı denetleyip tekrar deneyin.' }, }, undo: 'Geri al', undone: 'Geri alındı', }, en: { intro: 'Try the same message in three channels: inline alert, page banner and toast. Listen with a screen reader on.', inline: 'Inline alert (section)', banner: 'Banner (page-wide)', toast: 'Toast (transient)', tones: { success: 'Success', warning: 'Warning', danger: 'Error' }, kinds: { success: { title: 'Settings saved', text: 'Settings saved.' }, warning: { title: 'Storage almost full', text: 'About 80% of the storage is used.' }, danger: { title: 'Report not sent', text: 'The report was not sent. Check your connection and try again.' }, }, undo: 'Undo', undone: 'Undone', },};
type Tone = 'success' | 'warning' | 'danger';const tones: Tone[] = ['success', 'warning', 'danger'];
function ToneButtons({ label, lang, onPick }: { label: string; lang: Lang; onPick: (tone: Tone) => void }) { const t = copy[lang]; return ( <Stack gap="xs"> <Text size="sm" weight="medium"> {label} </Text> <Inline gap="sm" wrap> {tones.map((tone) => ( <Button key={tone} variant="secondary" size="sm" tone={tone === 'danger' ? 'danger' : 'neutral'} onClick={() => onPick(tone)}> {t.tones[tone]} </Button> ))} </Inline> </Stack> );}
function Lab({ lang }: { lang: Lang }) { const t = copy[lang]; const toast = useToast(); // A new `key` per pick remounts the message, so it is announced again and its own dismissal resets. const [inline, setInline] = useState<{ tone: Tone; key: number } | null>(null); const [banner, setBanner] = useState<{ tone: Tone; key: number } | null>(null);
const showToast = (tone: Tone) => { const { title, text } = t.kinds[tone]; // Only the success toast has an action: a short confirmation of the user's own action may offer undo. if (tone === 'success') { toast.show({ title, tone, action: { label: t.undo, onClick: () => toast.show({ title: t.undone, tone: 'neutral' }) } }); } else { toast.show({ title, description: text, tone }); } };
return ( <Stack gap="lg" className="w-full max-w-xl"> <Text size="sm" tone="subtle"> {t.intro} </Text> <ToneButtons label={t.inline} lang={lang} onPick={(tone) => setInline((prev) => ({ tone, key: (prev?.key ?? 0) + 1 }))} /> <ToneButtons label={t.banner} lang={lang} onPick={(tone) => setBanner((prev) => ({ tone, key: (prev?.key ?? 0) + 1 }))} /> <ToneButtons label={t.toast} lang={lang} onPick={showToast} /> <Stack gap="sm" className="min-h-12"> {banner ? ( <Banner key={`banner-${banner.key}`} tone={banner.tone} announce dismissible onDismiss={() => setBanner(null)} > {t.kinds[banner.tone].text} </Banner> ) : null} {inline ? ( <InlineAlert key={`inline-${inline.key}`} tone={inline.tone} title={t.kinds[inline.tone].title} announce dismissible onDismiss={() => setInline(null)}> {t.kinds[inline.tone].text} </InlineAlert> ) : null} </Stack> </Stack> );}
/** * A feedback lab for the decision matrix: a section message is an `InlineAlert`, a page-wide one a `Banner`, * a short confirmation of the user's own action a toast (the success toast offers "Undo"). * Messages that appear later are announced: errors assertively, the rest politely. */export function FeedbackLab({ lang = 'tr' }: { lang?: Lang }) { return ( <LangProvider lang={lang}> <Lab lang={lang} /> </LangProvider> );}The decision matrix maps a message to a channel by its scope.
| Channel | Scope and rule |
|---|---|
| Field error | Under the field; validation of that field only. |
Inline alert (InlineAlert) |
In a section; information, warning or error about that section. |
Banner (Banner) |
Page or system wide; persistent, stays until the user dismisses it or the condition ends. |
Toast (useToast) |
Short confirmation of the user’s own action; optional undo; auto-dismiss at 5000 ms or more, paused on hover and focus. Never for errors that need action. |
| Notification centre | Asynchronous events: jobs that finish while the user is elsewhere, invitations, reminders. |
| Severity | Icon | Text |
|---|---|---|
| Info | info |
“Info” |
| Success | circle-check |
“Success” |
| Warning | triangle-alert |
“Warning” |
| Danger | circle-x |
“Error” |
Every level pairs an icon with text; colour never carries the meaning alone.
| Live region | Use |
|---|---|
Polite (role="status") |
Status messages: success, warning, progress. |
Assertive (role="alert") |
Only errors that block. |
Behaviour
Section titled “Behaviour”- Decide the scope of the message: one field, a section, the page, a short confirmation or an asynchronous event.
- Pick the channel from the table; never give the same message through two channels.
- Pick the severity and add the icon and the word.
- If the message appears after the page has loaded, turn on
announce: the component announces an error assertively (role="alert") and the other severities politely (role="status"). A toast is announced in its own region. - A dismissible message has a dismiss button; dismissing fades the message out and moves focus to the next focusable element.
- A toast closes by itself and never holds critical information alone; the result is visible somewhere else too.
To guide users after an error, see the Error recovery pattern.
Accessibility
Section titled “Accessibility”- Status messages are written into a
role="status"region; screen readers read them without cutting off the current task. role="alert"is for blocking errors only; making every warning assertive tires the reader.InlineAlertandBannerspeak the severity with a hidden prefix (“Error:”, “Warning:”); a toast is announced in the notification region (“Notifications”). An error toast stays until it is closed.- The dismiss button has a name (“Dismiss”) and focus moves to the next focusable element.
- A toast pauses on hover and focus and lasts at least 5000 ms (WCAG 2.2.1).
- Colour never carries the meaning alone; an icon and a word are always present (WCAG 1.4.1).
Content
Section titled “Content”- A message says what happened; an error also says how to fix it.
- The component speaks the severity word (“Success:”); do not repeat it in the text: “Settings saved.”
- A toast is one sentence and holds no action except undo.
- Avoid blame and vague wording (“An error occurred”).
Motion
Section titled “Motion”| Item | Motion | Preset |
|---|---|---|
| Toast | In 240 ms, out 150 ms, 8 px shift | motion.toast |
| Field error text | 150 ms fade, 4 px drop | motion.fieldMessage |
| Inline message and Banner | Fade on dismiss | motion.collapsible |
With reduced motion the shifts are removed and only a short fade stays.
Components used
Section titled “Components used”InlineAlert, Banner, Toast, Button, Icon, Text, TextInput. Related patterns: Forms and validation, Error recovery.