AlertDialog
A modal dialog that asks a question. Same <dialog> and same top layer as
Dialog, with role="alertdialog" and a different set of buttons.
import { AlertDialog } from '@apostel/bedrock'
// or, for a veto: import { AlertDialog } from '@apostel/bedrock/controlled'
Source
import { useState } from 'react'
import { AlertDialog } from '../../src/index'
/**
* The same `<dialog>` as Dialog, with `role="alertdialog"` and no close button
* — an alert dialog asks a question, so both ways out are answers. Escape still
* works, because taking it away would trap a keyboard user in a decision.
*/
export default function AlertDialogDemo() {
const [result, setResult] = useState('')
return (
<>
<AlertDialog.Root>
<AlertDialog.Trigger>Delete project</AlertDialog.Trigger>
<AlertDialog.Content>
<AlertDialog.Title>Delete this project?</AlertDialog.Title>
<AlertDialog.Description>
Every deployment and log goes with it. This cannot be undone.
</AlertDialog.Description>
<AlertDialog.Cancel onClick={() => setResult('cancelled')}>Keep it</AlertDialog.Cancel>{' '}
<AlertDialog.Action onClick={() => setResult('deleted')}>Delete</AlertDialog.Action>
</AlertDialog.Content>
</AlertDialog.Root>
{result ? <output>{result}</output> : null}
</>
)
}Anatomy
<AlertDialog.Root>
<AlertDialog.Trigger />
<AlertDialog.Content>
<AlertDialog.Title />
<AlertDialog.Description />
<AlertDialog.Cancel />
<AlertDialog.Action />
</AlertDialog.Content>
</AlertDialog.Root>
Root, Trigger, Title and Description are Dialog's parts — the same
components, re-exported, because they behave identically. Only Content,
Cancel and Action differ.
AlertDialog.Content
Renders <dialog role="alertdialog">. Otherwise identical to
Dialog.Content: children mount only while open,
aria-labelledby and aria-describedby are wired to the title and description,
and id is not forwarded.
role="alertdialog" tells a screen reader to announce the whole dialog
immediately rather than just move focus into it, which is the behaviour a
destructive confirmation wants.
AlertDialog.Cancel / AlertDialog.Action
Both render <button>. Cancel closes with request-close, the same as
Dialog.Close; Action runs your handler and closes.
There is no AlertDialog.Close. An alert dialog asks a question, so both
ways out are answers — a third, meaningless dismissal would leave the caller not
knowing what the user decided.
Escape still closes, and deliberately so. Removing it would trap a keyboard user in a decision, and every platform's own alert dialogs allow it. Escape is a cancel: treat it as one.
What is not here
- A default focus on the destructive button. Focus lands where the browser
puts it for
showModal(), on the first focusable control. PutCancelfirst in the DOM if you want it focused, which is also the safer default. - Light dismiss. Same as Dialog: clicking outside does nothing, and here that is unambiguously right.