A message set into the page, such as a panel or a sheet, that says in plain sentences what is happening and how much it matters, in one of the intents' tinted colours, with an optional title, an action and a close button.
When to use
Use Alert for a message set into a panel or a sheet that says what is happening and how much it matters, and stays until the person or the data clears it, such as a signal fault.
| Use instead | When |
|---|---|
| Toast | The message confirms what just happened, such as "Link copied", and leaves on its own |
| SeverityLabel | The message is the kind of a disruption on a line |
| DisruptionCard | The message is one disruption on a line, with the stops it affects and when it runs |
| NormalService | The message is that a line runs normally |
| DataStatePanel | A data view is loading, empty or could not load, and the message stands in its place |
| EmptyState | A list, a search or a panel has nothing to show |
| AlertDialog | The person must answer a question before anything else |
Kongens Nytorv
Metro M1, M2 and M3.
Could not load departures
Variants and states
| Rest | |
|---|---|
| Neutral | Could not load departures The real-time feed stopped answering at 08:02. Departures show the timetable only. |
| Success | Could not load departures The real-time feed stopped answering at 08:02. Departures show the timetable only. |
| Warning | Could not load departures The real-time feed stopped answering at 08:02. Departures show the timetable only. |
| Danger | Could not load departures The real-time feed stopped answering at 08:02. Departures show the timetable only. |
| Info | Could not load departures The real-time feed stopped answering at 08:02. Departures show the timetable only. |
- Rest
Could not load departures
The real-time feed stopped answering at 08:02. Departures show the timetable only.
- Rest
Could not load departures
The real-time feed stopped answering at 08:02. Departures show the timetable only.
- Rest
Could not load departures
The real-time feed stopped answering at 08:02. Departures show the timetable only.
- Rest
Could not load departures
The real-time feed stopped answering at 08:02. Departures show the timetable only.
- Rest
Could not load departures
The real-time feed stopped answering at 08:02. Departures show the timetable only.
Props
| Name | Type | Default | Description |
|---|---|---|---|
color | 'neutral' | 'success' | 'warning' | 'danger' | 'info' | 'neutral' | The intent: what the message means. neutral for a plain notice, success for something that went well, warning for something to take care with, danger for something that failed or stops the task, info for something worth knowing; always in words that say it too. Set on the root as data-color |
title | ReactNode | — | The message in a few words, above the sentences: "Signal fault at Danshøj". Replaces the HTML title attribute |
children | ReactNode | — | What is happening and how sure we are, in plain sentences: "Trains between Flintholm and Ny Ellebjerg run slower than planned. We don't know yet when the fault will be fixed." |
icon | ReactNode | — | Replaces the intent's icon, for the eye only: it is hidden from screen readers. null or false shows no icon; left out, the intent's own shows, and neutral has none. It renders at 13 px, icon.size.325, so give an icon drawn on the 14 px grid, or set --perron-icon-size to its step's icon.size.* through slotProps.icon |
action | ReactNode | — | What the person can do about it, such as a small outline Button "Try again" or a LinkButton "Show on map", below the sentences |
onClose | (event: MouseEvent, reason: 'close-press', details: AlertCloseDetails) => void | — | Called when the close button is pressed, with the reason 'close-press'. Given, it shows the close button; Alert never hides itself, so remove it here and move focus to the element before it |
closeLabel | string | The locale's | Overrides "Close", the close button's name |
slotProps | { icon?: ComponentProps<"div", {}, HTMLProps> | undefined; content?: ComponentProps<"div", {}, HTMLProps> | undefined; title?: ComponentProps<"p", {}, HTMLProps> | undefined; description?: ComponentProps<"div", {}, HTMLProps> | undefined; action?: ComponentProps<"div", {}, HTMLProps> | undefined; close?: Omit<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "children" | "disabled"> | undefined; } | — | Props for each part: icon, content, title, description, action, close. |
color'neutral' | 'success' | 'warning' | 'danger' | 'info', default'neutral'- The intent: what the message means.
neutralfor a plain notice,successfor something that went well,warningfor something to take care with,dangerfor something that failed or stops the task,infofor something worth knowing; always in words that say it too. Set on the root asdata-color titleReactNode, default —- The message in a few words, above the sentences: "Signal fault at Danshøj". Replaces the HTML
titleattribute childrenReactNode, default —- What is happening and how sure we are, in plain sentences: "Trains between Flintholm and Ny Ellebjerg run slower than planned. We don't know yet when the fault will be fixed."
iconReactNode, default —- Replaces the intent's icon, for the eye only: it is hidden from screen readers.
nullorfalseshows no icon; left out, the intent's own shows, andneutralhas none. It renders at 13 px,icon.size.325, so give an icon drawn on the 14 px grid, or set--perron-icon-sizeto its step'sicon.size.*throughslotProps.icon actionReactNode, default —- What the person can do about it, such as a small outline Button "Try again" or a LinkButton "Show on map", below the sentences
onClose(event: MouseEvent, reason: 'close-press', details: AlertCloseDetails) => void, default —- Called when the close button is pressed, with the reason
'close-press'. Given, it shows the close button; Alert never hides itself, so remove it here and move focus to the element before it closeLabelstring, default The locale's- Overrides "Close", the close button's name
slotProps{ icon?: ComponentProps<"div", {}, HTMLProps> | undefined; content?: ComponentProps<"div", {}, HTMLProps> | undefined; title?: ComponentProps<"p", {}, HTMLProps> | undefined; description?: ComponentProps<"div", {}, HTMLProps> | undefined; action?: ComponentProps<"div", {}, HTMLProps> | undefined; close?: Omit<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "children" | "disabled"> | undefined; }, default —- Props for each part:
icon,content,title,description,action,close.
Use it
import { Alert } from '@mylder/perron'Ask your agent
search_docs({ query: "Alert" })Keyboard and ARIA
The alert takes no focus and has no keys of its own. The caller's action and the close button are reached in reading order; Esc does nothing, since an inline message is not a layer that closes (Accessibility, Keys).
| Key | Does | From |
|---|---|---|
Tab | Moves to the action's first control, then to the close button, after the sentences | The browser |
Shift+Tab | Moves back through them, and out to whatever came before the alert | The browser |
Enter, Space | On the close button, presses it: onClose is called with the reason 'close-press' | The browser; Perron calls onClose |
| Element | Role | Accessible name from | States announced |
|---|---|---|---|
root | status, a polite live region, unless the caller passes another role | None: a status needs no name | None |
icon | Hidden: aria-hidden="true" | None | None |
title | Paragraph, or a heading when slotProps.title.render makes it one | Its text, from title | None |
description | None (generic div) | None | None |
action | None (generic div); the caller's controls carry their own roles | The caller's controls' own names | Theirs |
close | button | The owned "Close", or closeLabel, in the VisuallyHidden part | None |
The root carries role="status" by default, for every color, unless the caller gives a role, so a caller can pass role="alert" for a danger message that stops the task the person is in; it is never alert by default. Accessibility asks for a polite live region that announces changes of state only, a status on a message present when the page loads is not read out and does no harm, a message whose words change while it stays mounted is read once, politely, and an alert on every danger message would interrupt whatever the person is hearing. A message added to the page with its words already in it is not reliably announced by a status (DataStatePanel keeps its region mounted for that reason), so CR-8 tells the caller what to do when one must be heard as it appears.
Alert adds no hidden word for its intent, such as "Warning" before the title; the caller's words say what the colour and the icon show, as Tag's and EmptyState's do: content design asks for plain sentences that say what is happening, a title such as "Could not load departures" already says it failed, and a fixed word before it would be read on every message, in four languages, and repeat what the sentence says ("Warning: Signal fault …").
What axe won't catch, from the contract, each with how this component meets it:
- Everything the eye is told, the ear is told, for every shape each prop accepts: the colour, the tint and the icon repeat what the caller's words say, and the icon is
aria-hidden; thecolorJSDoc and CR-2 ask for words that say the intent, and the stories hold to it. In forced colours the tint goes, and the edge, the icon's shape and the words stay. - Every landmark and named region has an accessible name: none here; a
statusis a live region, not a landmark, and needs no name. - Every default label can be overridden:
closeLabel. - Every shape the type accepts renders, in every variant:
title,children,iconandactioneach render a string, an element and an array;descriptionandactionaredivs, so block content such as a list is valid inside them, and the title takes phrasing content as apor a heading does;iconasnullorfalserenders no icon part (CR-3). - A hidden copy kept for animation is
aria-hidden, and a collapsed region isinert: none here; nothing animates or collapses. - Native semantics first: a
pfor the title, made a nativeh2toh6throughrender; the close button is a native<button type="button">; the action holds the caller's ownbuttonora href; the one ARIA is the root'srole="status", which no element says. - Focus moves in reading order and returns on close: the action follows the sentences and the close button comes last, in the DOM and in the tab order; Alert never moves focus when it appears, and once the caller removes it in
onClosethe caller moves focus to the element before it (CR-7). - Every target is at least
size.target-minsquare: the close button issize.control, 34 px; the action's targets are the caller's components, a small Buttonsize.button-sm. - Loops stop when the system asks for less motion: none here; nothing moves.
Owned strings
Alert owns one word, the close button's name, and reuses the key en.ts has, whose JSDoc already reads "A button that closes a dialog, panel or notice." Every other word comes from the caller, through title, children and action, and the icon is hidden from screen readers.
| Key | English | Override | Seen or heard where |
|---|---|---|---|
close | "Close" | closeLabel | close, heard as the button's name through the VisuallyHidden part, only when onClose is given; the key en.ts has |
Do and don't
For a caller using the component, with the reason. Each row is a rule: a must row's Don't is never right, a should row's needs a reason, and Checked by says what catches the Don't in a caller's code.
| Rule | Level | Do | Don't | Checked by |
|---|---|---|---|---|
| DD-1 | must | Say what is happening, then how sure we are, in plain sentences: a title such as "Signal fault at Danshøj" above a sentence such as "Trains between Flintholm and Ny Ellebjerg run slower than planned. We don't know yet when the fault will be fixed." | Write "Error!" or "Something went wrong": content design has no exclamation marks, and the title should say what failed | review |
| DD-2 | must | Pick the color the words say: success for something that went well, warning for something to take care with, danger for something that failed | Let the colour or the icon be the only sign: every message says its meaning in words too | review |
| DD-3 | should | Use SeverityLabel or a DisruptionCard for a disruption on a line, and NormalService for a line running normally | Use an Alert for each disruption: a disruption has its own words, icons and colours, and a list of tinted boxes is not calm | review |
| DD-4 | should | Use DataStatePanel or EmptyState when a data view is loading, empty or could not load | Put an Alert in place of the data view's content: it sits beside content, it doesn't stand in for it | review |
| DD-5 | must | Give one action at most, a small outline neutral Button or a LinkButton | Add a brand action: a screen has one brand action at most, and the message is not it | review |
| DD-6 | must | Remove the alert in onClose and move focus to the element before it | Leave focus on a close button that is gone: it drops to the top of the page | review |
| DD-7 | must | Add a message that must be heard as it appears inside a live region already on the page, or pass role="alert" for a failure that stops the task | Pass role="alert" on every danger message, or add a live region of your own around an Alert shown on load: it interrupts, or adds noise | review |
| DD-8 | must | Show a message that comes and goes on its own, such as "Link copied", as a Toast | Make an Alert disappear on a timer: an inline message stays until the person or the data says otherwise | review |