Patterns
Messages: success, information, warning and error
The four tonesLink to the section The four tones
Success
It worked, and the result is what the reader asked for.
Success: Invitation sent to ada@example.com
- Read out
- Polite
- Stays
- A toast may close after 5 s or more
Information
A neutral fact that helps, with nothing to fix.
Information: Imports run at night
- Read out
- Polite
- Stays
- A toast may close after 5 s or more
Warning
Something may go wrong or needs a decision soon. Nothing is blocked yet.
Warning: Your draft is kept for 7 more days
- Read out
- Polite
- Stays
- Until closed
Error
It failed or is blocked. Say what happened and how to fix it.
Error: Could not publish. The server did not answer.
- Read out
- Assertive
- Stays
- Until closed or fixed
Each tone has a colour, an icon of its own shape and a word. The word, such as “Error”, is the toneLabel prop, read before the message so the meaning never rests on colour. Pass it every time.
Choose the right componentLink to the section Choose the right component
| Component | Where | How long | How it is read | Use it for |
|---|---|---|---|---|
| Inline field error | Under the field it is about | Until the value is fixed | With the field, through aria-describedby | One field that needs a change |
| FormErrorSummary | At the top of the form | Until the next submit | Assertive, and it takes focus | A submit that failed on one or more fields |
| Alert | In the content, next to what it is about | Until closed, or while the state lasts | Static, or polite or assertive with live | A result or condition of one part of the page |
| Banner | Full width at the top of a page or panel | As long as the state lasts | A named region; live when it appears later | A state of the whole app, such as offline or read-only |
| Toast | At the edge of the screen | Success and information at least 5 s; warnings and errors until closed | Polite, or assertive for errors | The result of an action just taken, when it is not visible elsewhere |
| FlashMessages | At the top of the next page | Until closed | Written into a live region after the page loads | A server result after a redirect, such as “Saved” |
| FormActions status | Next to the submit buttons | Until the next submit | Polite, or assertive for errors | The save result of a short form |
A quick rule: the closer the message is to its cause, the better. A field error beats a summary, a summary beats a banner, and a toast is for results the reader would otherwise not see.
Live examplesLink to the section Live examples
Alert with a live result
The live region is in the page before the result arrives, so the result is read out when it appears.
<div aria-live="polite">
{result && <Alert tone="ok" toneLabel={t('tone.ok')} title={t('tracker.connected')} />}
</div>Banner for a state of the whole app
Error: You are offline
Warning: Read-only mode
Page content
<Banner tone="danger" toneLabel={t('tone.error')} title={t('offline.title')} live="assertive">
{t('offline.text')}
</Banner>Toasts for the result of an action
const toast = useToast()
toast.success(t('saved'))
toast.error(t('publish.failed'), { description: t('publish.retry') })Flash messages after a redirect
Success: Profile saved
<FlashMessages messages={messagesFromServer} toneLabels={toneLabels} onDismiss={remove} dismissLabel={t('close')} />Inline errors and the error summary
Submit the empty form. The summary takes focus and links to each field; each field shows its own error. Fix them and submit again for the success message.
<FormErrorSummary title={t('form.problems', { count })} errors={errors} />
<TextField id="name" label={t('name')} isInvalid={!!nameError} errorMessage={nameError} />Writing guidelinesLink to the section Writing guidelines
- Say what happened, then what to do. “Could not save. Check your connection and try again.”
- Be specific. Name the thing and the value: “Enter a date after 1 March”, not “Invalid date”.
- Use plain words and no blame. “That email address is already in use”, not “You entered an invalid email”. No codes unless support needs them, and then after the words.
- Keep the title short. Up to about eight words. Put the detail in the description.
- Repeat the verb of the button. After “Publish”, say “Published”, so the reader connects the two.
- Stay calm. No “Oops”, no exclamation marks, no jokes in errors, and one apology at most when Skribly is at fault.
- Warnings say the consequence and the way out. “Your plan ends in 3 days. Renew it to keep editing.”
Error: Could not publish the page
Error: Oops! Something went wrong
Success: Invitation sent to ada@example.com
Success: Success!
Timing and focusLink to the section Timing and focus
- Errors and warnings never close on their own. Success and information toasts stay at least 5 seconds and pause while the pointer or focus is on them.
- Move focus only for a blocking error after a submit, as FormErrorSummary does. Everything else is announced and leaves focus where it is.
- Announce each message once. Render the live region empty first, then fill it; a region that appears with its text is often not read.
- Put a toast’s action somewhere else too. A toast may close before a keyboard user reaches it, so Undo needs a second path, such as the item’s menu.
import { announce } from '@skribly/ui/theme'
announce(t('results', { count })) // polite
announce(t('upload.failed'), 'assertive') // errors only