AlertDialog
A modal that demands a decision — usually about something destructive or irreversible — and refuses to be dismissed by accident.
Basic usage
Install the package:
pnpm add @design-schema/react @design-schema/tokens Import the stylesheet once for the app, then the component:
import '@design-schema/tokens/calm-precise/css';
import '@design-schema/react/index.css';
import { AlertDialog } from '@design-schema/react'; Examples
import { useEffect, useState, type ReactElement } from 'react';
import { AlertDialog, type AlertDialogProps, Button } from '@design-schema/react';
function AlertDialogHarness({ open: initialOpen, onConfirm, onCancel, ...rest }: AlertDialogProps): ReactElement {
const [open, setOpen] = useState(initialOpen);
useEffect(() => setOpen(initialOpen), [initialOpen]);
return (
<>
<Button label={rest.heading} onClick={() => setOpen(true)} />
<AlertDialog
{...rest}
open={open}
onConfirm={() => {
onConfirm?.();
setOpen(false);
}}
onCancel={(reason) => {
onCancel?.(reason);
setOpen(false);
}}
/>
</>
);
}
<AlertDialogHarness
open
heading="Delete 3 files?"
description="They will be removed from all shared folders. This cannot be undone."
confirmLabel="Delete files"
/>Props
7 rows| Prop | Type | Default | Description |
|---|---|---|---|
| open required | boolean | — | Controlled only — there is no uncontrolled mode; the consumer owns `open` and sets it false after handling `onConfirm` or `onCancel`, as in Dialog. |
| heading required | string | — | The question or statement, as a level-2 Heading at the size Heading reads from level 2 (no explicit `size`), and the accessible name ("Delete 3 files?"). Accessibility: aria-labelledby the heading; native accessibilityLabel on the modal content. |
| description required | string | — | What will happen and whether it can be undone, in one or two sentences, rendered as Text `tone="muted"` at Text's default size (the color.foreground.muted contrast pair). Required: a decision without consequences stated is not a decision. Accessibility: aria-describedby; announced together with the title when the dialog opens. |
| tone | danger | warning | info | danger | The nature of the decision. Sets the status icon (Icon `name` equal to the tone) and the confirm button's variant (danger → danger Button; warning and info → primary). Both buttons are Button size md. |
| confirmLabel required | string | — | The confirming action, restating it ("Delete files"). Never "OK" or "Yes". |
| cancelLabel | string | — | The declining action. Defaults to `copy.cancelLabel`. |
| confirmDisabled | boolean | false | Blocks confirm while a precondition is unmet (a typed confirmation, a loading state). Cancel always works. Forwarded to the confirm Button's own `disabled` — the Button decides what that means per platform, and on every platform it stays focusable-but-inert (aria-disabled plus a click guard on web and Lit, accessibilityState.disabled plus a press guard on native), so Confirm is still the last Tab stop; AlertDialog neither restyles it nor sets aria-disabled itself. |
Events
2 rows| Event | Type | Description |
|---|---|---|
| onConfirm | onConfirm / confirm | The user chose the confirming action. The consumer performs it and closes. |
| onCancel | onCancel / cancel | The user declined, by the cancel button or Escape. Fired with reason `cancel` or `escape`. A scrim click does nothing. The `close-button` entry in `overlay.dismiss` is the shared category name for the Cancel button, reported as `cancel`; there is no separate close button. |
Accessibility
Decisions this component leaves to you
Supply an accessible name. This component has no text of its own to fall back on, so without one a screen reader announces its role and nothing else.
Required props that carry accessibility meaning: heading, description. Each is described in the props table above.
Implicit ARIA role: alertdialog.
Anatomy
What generation guarantees
Accessible name — Every instance carries a programmatic name. Where the component has no text of its own to use, the name is a prop the consumer supplies.
Focus trap — While it is open, Tab cycles inside it; focus cannot reach the page behind.
Focus restore — On close, focus returns to whatever opened it.
Escape dismisses — Escape closes it from anywhere inside.
Inert background — While it is open, the rest of the page is inert to the pointer and to assistive technology.
Scroll lock — While it is open, the page behind it does not scroll.
Keyboard operable — Everything the pointer can do here, the keyboard can do — see the keyboard section.
Visible focus — Keyboard focus is always drawn, from the theme’s focus tokens, and is never removed.
Contrast — Text and meaningful non-text contrast meets WCAG AA; the pairs are listed below.
Reduced motion — Under `prefers-reduced-motion: reduce` the transitions are dropped rather than shortened, and the component still reaches every state.
Target size — Interactive targets are at least 24×24 CSS pixels (WCAG 2.2 Target Size, minimum).
Under `prefers-reduced-motion: reduce` the transitions are dropped rather than shortened, and the component still reaches every state.