Alert Dialog
A modal confirmation that requires an explicit user response before a destructive action.
Show codeHide code
Installation
import {
AlertDialog,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogBody,
AlertDialogHeader,
AlertDialogIcon,
AlertDialogIntro,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogActions,
AlertDialogActionGroup,
AlertDialogCloseGroup,
AlertDialogClose,
} from "@makeplane/propel/components/alert-dialog";Usage
Compose a 600px card: AlertDialogBody stacks the intent badge (AlertDialogIcon) above the
title/description (AlertDialogIntro), optional fields go in the body under the header, and
AlertDialogActions is the flush footer. Pin a corner dismiss with AlertDialogCloseGroup +
IconButton rendering AlertDialogClose.
Escape cancels the topmost alert and restores focus to its trigger. Clicking outside leaves it open.
Cancel and the corner dismiss should produce the same non-destructive outcome. Consumers can veto
a close with onOpenChange and details.cancel() while an operation is pending.
Below 640px, the card bottom-aligns with 16px viewport gutters, the badge grows to 48px, the intro centers, and actions stack in reverse order at full width. Larger screens retain the centered 600px Figma card. This retains the legacy mobile behavior approved in AD-F10.
Use Button variant="secondary" size="md" for Cancel or the safe alternative, danger for an
irreversible action, and primary for other confirmations. Reserve ghost for an optional third
action. Keep the footer to three actions at most. Within this footer, secondary and ghost buttons
at the recommended size use the Figma 14px label; primary and danger retain 13px. Buttons outside
Alert Dialog retain their normal typography. The alert scrim is black at 25% opacity.
Use AlertDialogActions layout="split" with a leading alternative and an AlertDialogActionGroup
containing Cancel and Confirm. The default layout="inline" retains the end-aligned row. Both
layouts reverse into full-width actions below 640px. When a leading alternative comes before
Cancel in DOM order, pass the Cancel button ref as AlertDialogContent initialFocus.
<AlertDialogActions layout="split">
<Button variant="ghost" size="md" stretch="auto" label="Discard changes" />
<AlertDialogActionGroup>
<Button
variant="secondary"
size="md"
stretch="auto"
label="Cancel"
render={<AlertDialogClose />}
/>
<Button variant="primary" size="md" stretch="auto" label="Save changes" />
</AlertDialogActionGroup>
</AlertDialogActions>The design review’s AD-F10 decision takes precedence over the reference guidance’s “always center” wording on mobile; this component intentionally preserves the approved responsive behavior.
On open, focus lands on the first action button. If the body can scroll, give AlertDialogBody
tabIndex={0} so keyboard users can reach the scroll region, and pass initialFocus to
AlertDialogContent to keep the opening focus on Cancel.
import {
AlertDialog,
AlertDialogActions,
AlertDialogBody,
AlertDialogClose,
AlertDialogCloseGroup,
AlertDialogContent,
AlertDialogDescription,
AlertDialogHeader,
AlertDialogIcon,
AlertDialogIntro,
AlertDialogTitle,
AlertDialogTrigger,
} from "@makeplane/propel/components/alert-dialog";
import { Button } from "@makeplane/propel/components/button";
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { CloseOutline } from "@makeplane/propel/icons";
export default function BasicDemo() {
return (
<AlertDialog>
<Button
stretch="auto"
variant="danger"
size="md"
render={<AlertDialogTrigger />}
label="Delete project"
/>
<AlertDialogContent>
<AlertDialogBody>
<AlertDialogHeader>
<AlertDialogIcon variant="danger" />
<AlertDialogIntro>
<AlertDialogTitle>Delete project?</AlertDialogTitle>
<AlertDialogDescription>
This permanently removes the project and all of its work items. This action
can't be undone.
</AlertDialogDescription>
</AlertDialogIntro>
</AlertDialogHeader>
</AlertDialogBody>
<AlertDialogActions>
<Button
stretch="auto"
variant="secondary"
size="md"
render={<AlertDialogClose />}
label="Cancel"
/>
<Button
stretch="auto"
variant="danger"
size="md"
render={<AlertDialogClose />}
label="Delete"
/>
</AlertDialogActions>
<AlertDialogCloseGroup>
<IconButton
variant="ghost"
size="xs"
aria-label="Close"
render={<AlertDialogClose />}
icon={<Icon icon={CloseOutline} />}
/>
</AlertDialogCloseGroup>
</AlertDialogContent>
</AlertDialog>
);
}Examples
Variants
The leading AlertDialogIcon badge sets the confirmation’s intent through its required variant
— danger, warning, info, or success. It uses the matching Plane outline status glyph. Use a danger primary action for destructive confirmations; other intents typically
use primary. Use a toast or inline message for passive success information.
Show codeHide code
Rich descriptions
AlertDialogDescription defaults to a paragraph. Use render={<div />} when its content includes
paragraphs or a list; styling and the popup’s accessible description remain connected. Put controls
and larger interactive content in AlertDialogBody, outside the description.
Long content
The body caps at 680px and scrolls while the actions stay pinned. A scrollable region has to be
keyboard-reachable, so pass tabIndex={0} on AlertDialogBody. That puts the body first in tab
order, so also pass initialFocus to AlertDialogContent pointing at Cancel; otherwise the dialog
opens focused on the scroll region.
Show codeHide code
Open from menu
An alert dialog opened by a menu item is controlled through open / onOpenChange rather than an AlertDialogTrigger.
Show codeHide code
Multiple triggers
createAlertDialogHandle connects many detached triggers to one shared confirmation, each passing the project it acts on as its payload.
Show codeHide code
API Reference
AlertDialog
Modal confirmation root. Escape dismisses the topmost alert as a cancellation, matching Plane's
existing behavior. Outside presses do not dismiss it. Consumers can veto a close through
onOpenChange and details.cancel(), for example while a request is pending.
Base UI owns focus trapping, nested dismissal, and focus restoration. Explicit close controls,
actionsRef.close(), and controlled open state remain available.
AlertDialogTrigger
The behavior that opens the alert dialog. Use as the render target of a Button so the styled
primitive's look wins via render-composition:
``tsx
<Button variant="danger" size="lg" label="Delete project" render={<AlertDialogTrigger />} />;
Base UI manages aria-haspopup/aria-expanded and focus restoration when the alert dialog
closes. Maps 1:1 to AlertDialog.Trigger`.
AlertDialogContent
Convenience that composes the alert dialog overlay boilerplate — Base UI portal, the 25% black
backdrop, the responsive viewport, and the popup — so a consumer only writes the trigger and the
popup body.
Initial focus follows Base UI: the first tabbable element in the popup, which in the default
anatomy is the first action button (Cancel). Pass initialFocus (a ref, or a function of the
interaction type) to steer it — for example onto Cancel when a scrollable AlertDialogBody with
tabIndex={0} would otherwise come first in DOM order. finalFocus overrides where focus
returns on close (default: the trigger).
Nested alerts keep their own 25% scrim: Base UI omits a nested Backdrop unless forceRender is
set, which would leave a confirmation sitting on its parent dialog with no extra overlay. Closed
force-rendered backdrops set pointer-events: none so they cannot steal clicks from the parent
while unmounting.
AlertDialogBody
The padded content region of the alert dialog popup (20px top and sides, 12px bottom). Stacks the
header (icon + intro) and any optional body content (form fields, extra copy) with a 16px gap.
Caps height so tall content scrolls while the actions footer stays pinned. Wide descendants are
constrained by an inner overflow-x-clip wrapper so focus scrolling cannot pan sideways.
Not focusable by default, so the dialog's initial focus lands on the first action button rather
than on a region nothing can be done with (Dialog pattern). When the body can overflow, pass
tabIndex={0} so the scroll region is keyboard-reachable (WCAG 2.1.1); pair it with
initialFocus on AlertDialogContent if focus should still start on an action.
AlertDialogHeader
Groups the leading AlertDialogIcon badge above the AlertDialogIntro (title + description).
Lives inside AlertDialogBody. Pass icon and intro as children.
AlertDialogIcon
The leading intent badge above the title. Soft tinted surface + outline status glyph selected
from variant — callers never pass an icon (matches the Alert Dialog Figma family). Decorative
(aria-hidden) — the title carries the accessible name.
variant is required (danger | warning | info | success).
AlertDialogIntro
Groups the title and description with a 6px gap. Pass AlertDialogTitle and
AlertDialogDescription as children.
AlertDialogTitle
The accessible title for the alert dialog. Base UI links it to the popup via aria-labelledby;
the styled elements title supplies the h6/semibold look.
AlertDialogDescription
The supporting description for the alert dialog. Base UI links it to the popup via
aria-describedby; the styled elements description supplies the body-sm / secondary look.
Defaults to a paragraph. Pass render={<div />} for block content such as an impact list; both
the description styling and Base UI accessibility wiring are preserved.
AlertDialogActions
The footer row of action buttons. Sits flush under the body with no divider, pads 12px on top and 20px on the sides and bottom (Figma), and end-aligns children with a 12px gap. Below 640px, actions form a reversed full-width column, retaining the legacy mobile order. Layout groups actions at the end, or separates a leading action from an action group.
AlertDialogActionGroup
Keeps related footer actions together in a split footer.
AlertDialogCloseGroup
Positions the corner dismiss control at the popup's top-end. Pass an IconButton (components
tier) or the styled AlertDialogClose (elements showcase) as the child.
AlertDialogClose
The behavior that closes the alert dialog when activated. Use as the render target of a
Button so the styled primitive's look wins via render-composition:
``tsx
<Button variant="secondary" size="lg" label="Cancel" render={<AlertDialogClose />} />;
Maps 1:1 to AlertDialog.Close`.