A modal question over the map and a color.scrim backdrop, such as "Clear all filters?", that one of its actions or Esc answers: it moves focus to the first of its actions, Cancel, wraps Tab inside the popup while focus is in it, and on close gives focus back to the button that opened it.
When to use
Use AlertDialog for a question the person must answer before anything else, such as "Clear all filters?" before the filters are cleared.
Kongens Nytorv
Metro M1, M2 and M3.
Props
| Name | Type | Default | Description |
|---|---|---|---|
title* | ReactNode | — | The question, in sentence case, such as "Clear all filters?". Required: it names the alert dialog for screen readers. |
description* | ReactNode | — | One sentence under the title that says what answering does, such as "The map shows every line again.", read out with the name when it opens. Required. Phrasing content only: it renders in a <p>. |
actions* | ReactNode | — | The buttons that answer the question, such as Buttons, in a row at the alert dialog's end: Cancel first, which takes focus when it opens, and the confirming action last. An action closes the alert dialog by setting open to false, which calls no onClose. Required: with no close button, Cancel is the way out for touch screen readers. |
trigger | ReactElement<unknown, JSXElementConstructor<any> | string> | — | The element that opens the alert dialog, such as a Button, rendered in place of the alert dialog. Focus returns to it when the alert dialog closes. Leave it out for an alert dialog opened only through open. |
open | boolean | — | Whether the alert dialog is open. Set it to control the alert dialog, with onClose to hear when it asks to close. An alert dialog whose actions must close it is controlled with open: a button in actions closes it only by setting open to false. |
defaultOpen | boolean | false | Whether the alert dialog is open at first, when open is left out. Uncontrolled, its actions cannot close it, since they close it through open: it closes by Esc, or by Enter or Space on the trigger, which Shift+Tab reaches after a press on the backdrop among the page's other tabbable elements before the portal, so an alert dialog whose actions must close it is controlled with open instead. |
onOpen | (event: Event, details: AlertDialog.Root.ChangeEventDetails) => void | — | Called when the alert dialog asks to open, from its trigger. details is Base UI's, with its reason and cancel(). |
onClose | (event: Event, reason: AlertDialog.Root.ChangeEventReason, details: AlertDialog.Root.ChangeEventDetails) => void | — | Called when the alert dialog asks to close, by Esc, with reason 'escape-key', or by Enter or Space on the trigger, which Shift+Tab reaches after a press on the backdrop among the page's other tabbable elements before the portal, with 'trigger-press'; details.cancel() keeps it open. A button in actions closes it through open and calls no onClose. |
className | string | — | Reach the root, the popup. className is merged after pn-AlertDialog. |
style | CSSProperties | — | Reach the root, the popup. className is merged after pn-AlertDialog. |
ref | Ref<HTMLDivElement> | — | Reach the root, the popup. className is merged after pn-AlertDialog. |
slotProps | { trigger?: Props<unknown> | undefined; portal?: DialogPortalProps | undefined; backdrop?: DialogBackdropProps | undefined; title?: DialogTitleProps | undefined; description?: DialogDescriptionProps | undefined; } | — | Props for each part: trigger, portal, backdrop, title, description. |
title (required)ReactNode, default —- The question, in sentence case, such as "Clear all filters?". Required: it names the alert dialog for screen readers.
description (required)ReactNode, default —- One sentence under the title that says what answering does, such as "The map shows every line again.", read out with the name when it opens. Required. Phrasing content only: it renders in a
<p>. actions (required)ReactNode, default —- The buttons that answer the question, such as Buttons, in a row at the alert dialog's end: Cancel first, which takes focus when it opens, and the confirming action last. An action closes the alert dialog by setting
opentofalse, which calls noonClose. Required: with no close button, Cancel is the way out for touch screen readers. triggerReactElement<unknown, JSXElementConstructor<any> | string>, default —- The element that opens the alert dialog, such as a Button, rendered in place of the alert dialog. Focus returns to it when the alert dialog closes. Leave it out for an alert dialog opened only through
open. openboolean, default —- Whether the alert dialog is open. Set it to control the alert dialog, with
onCloseto hear when it asks to close. An alert dialog whose actions must close it is controlled withopen: a button inactionscloses it only by settingopentofalse. defaultOpenboolean, defaultfalse- Whether the alert dialog is open at first, when
openis left out. Uncontrolled, its actions cannot close it, since they close it throughopen: it closes by Esc, or by Enter or Space on the trigger, which Shift+Tab reaches after a press on the backdrop among the page's other tabbable elements before the portal, so an alert dialog whose actions must close it is controlled withopeninstead. onOpen(event: Event, details: AlertDialog.Root.ChangeEventDetails) => void, default —- Called when the alert dialog asks to open, from its trigger.
detailsis Base UI's, with itsreasonandcancel(). onClose(event: Event, reason: AlertDialog.Root.ChangeEventReason, details: AlertDialog.Root.ChangeEventDetails) => void, default —- Called when the alert dialog asks to close, by Esc, with
reason'escape-key', or by Enter or Space on the trigger, which Shift+Tab reaches after a press on the backdrop among the page's other tabbable elements before the portal, with'trigger-press';details.cancel()keeps it open. A button inactionscloses it throughopenand calls noonClose. classNamestring, default —- Reach the root, the popup.
classNameis merged afterpn-AlertDialog. styleCSSProperties, default —- Reach the root, the popup.
classNameis merged afterpn-AlertDialog. refRef<HTMLDivElement>, default —- Reach the root, the popup.
classNameis merged afterpn-AlertDialog. slotProps{ trigger?: Props<unknown> | undefined; portal?: DialogPortalProps | undefined; backdrop?: DialogBackdropProps | undefined; title?: DialogTitleProps | undefined; description?: DialogDescriptionProps | undefined; }, default —- Props for each part:
trigger,portal,backdrop,title,description.
Use it
import { AlertDialog } from '@mylder/perron'Ask your agent
get_reference({ library: "base-ui", component: "AlertDialog" })Keyboard and ARIA
From the Base UI reference for AlertDialog and the installed useRenderDialogRoot, useDialogRoot, DialogPopup, DialogTitle and DialogDescription. "From" says who does it.
| Key | Does | From |
|---|---|---|
Enter, Space on the trigger | Open the alert dialog, calling onOpen with reason 'trigger-press', and move focus to the first tabbable element in the popup, the first button in actions, Cancel, unless initialFocus says otherwise | The browser activates the button; Base UI opens and moves focus |
Tab, Shift+Tab | Move focus through the buttons in actions, in reading order, wrapping from the last to the first and back while focus is in the popup. After a press on the backdrop, which moves focus to <body>, Tab lands on the last button in actions, the confirming one, and Shift+Tab reaches the page's tabbable elements before the portal, each behind the scrim and aria-hidden but focusable and operable: the trigger is one of them, and in the stories the only one | Base UI |
Enter, Space on the trigger while open | Reached only by Shift+Tab after a press on the backdrop, past any tabbable element that follows the trigger on the page: close the alert dialog, calling onClose with reason 'trigger-press', focus staying on the trigger | The browser activates the button; Base UI closes |
Escape | Closes the topmost alert dialog, calling onClose with reason 'escape-key', and returns focus to the trigger | Base UI |
Enter, Space on a button in actions | Run its onClick, in which the caller sets open to false, closing the alert dialog with no onClose; focus returns to the trigger | The browser activates the button; the caller acts and closes; Base UI returns focus |
A press on the backdrop closes nothing, since Base UI's alert dialog has no outside-press dismissal, but it moves focus out of the popup to <body>, as the Tab row says.
| Element | Role | Accessible name from | States announced |
|---|---|---|---|
trigger | button, native, or the caller's element's own | Its own content, from the caller | aria-haspopup="dialog", aria-expanded, from Base UI |
root | alertdialog, from Base UI | aria-labelledby the title; described by the description through aria-describedby | Modal by Base UI hiding everything outside it with aria-hidden |
title | heading, level 2, from the native <h2> | Its text, the caller's title | none |
A button in actions | button, native, or the caller's element's own | Its own content, from the caller | Disabled, when the caller disables it |
backdrop | presentation, from Base UI | none | none |
description, actions | none: a plain <p> and a plain <div> | none | none |
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 alert dialog is announced as an alert dialog with its
titleas its name and itsdescription, the buttons inactionsshow their own words, and the scrim only dims what thearia-hiddenoutside the popup already removes. - Every landmark and named region has an accessible name: the alert dialog is named by its own heading, the
title, througharia-labelledby, andtitleis required. - Every default label can be overridden: none here; AlertDialog owns no string, and every word, Cancel's included, is the caller's.
- Every shape the type accepts renders, in every variant:
title,descriptionandactionsrender a string, an element and a list alike; a long word in the title or the description wraps, and the buttons inactionswrap onto more lines when the row is too narrow (CR-9). - A hidden copy kept for animation is
aria-hidden, and a collapsed region isinert: none here; the closed alert dialog renders nothing, and Base UI hides the page behind the open one witharia-hidden. - Native semantics first: Base UI's
alertdialogrole, a native<h2>for the title, native<button>s for the trigger and the caller's Buttons inactions, and no ARIA of Perron's own. - Focus moves in reading order and returns on close: the title comes first in the DOM, then the description and the buttons in
actions, Cancel first, as on screen; opening moves focus to Cancel, the answer that changes nothing, and closing, by Esc or by the caller'sopen, returns it to the trigger, also for an alert dialog open at first render (CR-5). After a press on the backdrop, Base UI moves focus to<body>, and the next Tab lands on the last button inactions, not on Cancel, and Shift+Tab reaches the page's tabbable elements before the portal, eacharia-hiddenbut focusable and operable, the trigger among them (Keyboard and ARIA). - Every target is at least
size.target-minsquare: the buttons inactionsare the caller's, and a Button at itsmediumsize issize.button(36 px) tall and wider than tall. - Loops stop when the system asks for less motion: none here; the alert dialog has no loop and no transition (CR-10).
Owned strings
No owned strings: every word is the caller's, through title, description, actions and trigger, Cancel's words included, as in Dialog's actions, so AlertDialog adds no key to packages/perron/src/locale/en.ts and takes no <key>Label prop.
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 | Ask one question in the title, in sentence case, such as "Clear all filters?", and say in the description what answering does | Write a title that isn't a question, such as "Warning", or leave the consequence out: the person can't answer what they weren't asked | review |
| DD-2 | must | Name the action by what it does, such as "Clear filters", and make it the one brand Button, last in actions | Name it "OK" or "Yes": the button must say what pressing it does, read on its own | review |
| DD-3 | should | Use an alert dialog for an answer that must come before anything else, such as before clearing or removing something | Use it for help, a form or a list: those are a Dialog's, which a press outside closes | review |
| DD-4 | must | Put Cancel first in actions, an outline neutral Button, so it takes focus when the alert dialog opens and Enter on its own changes nothing | Put the action first, or set initialFocus to it: a stray Enter would act at once | review |
| DD-5 | must | Control the alert dialog with open, and close it from each button's onClick by setting open to false, once the action's work is done | Leave Cancel out, disable it or hide it with a className: with no close button, it is the way out for touch screen readers, which have no Esc | review |
| DD-6 | should | Pass the button that opens the alert dialog as trigger, so it is announced as opening a dialog and gets focus back | Open an alert dialog on its own, with no press from the person, to tell them something: a notice that needs no answer is an Alert or a Toast | review |
| DD-7 | should | Name Cancel by what it keeps when that reads better, such as "Keep filters", in the screen's locale | Add a close button or a second cancelling button to actions: Cancel and Esc already leave without acting | review |
| DD-8 | must | Let the alert dialog take z-index.dialog and its place in the middle of the screen | Set a z-index, a position or an opacity on it: it would fall under another overlay or leave its scrim | review |