Alert Dialog

A modal dialog that interrupts the user with important content and expects a response.

Spec · from metadata

When to use

  • •Confirming destructive actions (delete, remove, discard changes)
  • •Blocking decisions that require explicit user acknowledgment before proceeding
  • •Irreversible operations where the user must understand consequences
  • •Logout or session-ending confirmations

When not to use

  • •Non-destructive confirmations -- use Dialog instead
  • •Informational messages that don't require action -- use Alert (inline) or Toast (transient)
  • •Form collection or complex interactions -- use Dialog or Sheet
  • •Mobile-first bottom panels -- use Drawer instead

Variants

PropValuesDefaultDescription
sizedefaultsmdefaultControls dialog width. 'sm' gives a narrower max-w-xs layout with grid-based footer; 'default' gives max-w-lg on sm+ screens.

Anti-patterns

Avoid<Dialog>
  <DialogContent>
    <DialogTitle>Delete this item?</DialogTitle>
    <Button variant="destructive">Delete</Button>
  </DialogContent>
</Dialog>
Prefer<AlertDialog>
  <AlertDialogContent>
    <AlertDialogTitle>Delete this item?</AlertDialogTitle>
    <AlertDialogAction variant="destructive">Delete</AlertDialogAction>
    <AlertDialogCancel>Cancel</AlertDialogCancel>
  </AlertDialogContent>
</AlertDialog>

Destructive confirmations must use AlertDialog, not Dialog. AlertDialog traps focus and prevents dismissal by clicking the overlay, ensuring the user makes an explicit choice.

Avoid<AlertDialogContent>
  <AlertDialogHeader>
    <AlertDialogTitle>Confirm</AlertDialogTitle>
  </AlertDialogHeader>
</AlertDialogContent>
Prefer<AlertDialogContent>
  <AlertDialogHeader>
    <AlertDialogTitle>Delete this project?</AlertDialogTitle>
    <AlertDialogDescription>
      This will permanently delete "My Project" and all associated data.
    </AlertDialogDescription>
  </AlertDialogHeader>
</AlertDialogContent>

Always include AlertDialogDescription for accessibility and clarity. Screen readers announce both title and description.

Accessibility

  • Required ARIAaria-labelledby (via AlertDialogTitle)aria-describedby (via AlertDialogDescription)
  • Screen readerAnnounced as an alert dialog. The title and description are automatically associated via Radix primitives. Overlay click does not dismiss -- users must use Cancel or Action.
  • ContrastOverlay uses bg-foreground/50 for sufficient contrast against page content.

Token bindings

TokenCategoryUsage
bg-cardcolorDialog content background
bg-foreground/50colorOverlay background
text-muted-foregroundcolorDescription text
bg-mutedcolorMedia slot background
shadow-lgshadowDialog elevation
rounded-lgradiusDialog border radius
duration-200motionOpen/close animation duration

Import

import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogMedia,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@timelycare/helix-ui"

Variants

BreakpointViewportHeader AlignmentFooter LayoutButton WidthButton Order
md≥768pxLeft-alignedHorizontal, right-alignedAutoCancel, Action
sm<768pxCenter-alignedStacked, full-width100%Action, Cancel

Choosing a Variant

  • Use responsive footer classes to automatically switch: flex-col sm:flex-row sm:justify-end
  • Desktop: Cancel first, then Action (reading order, right-aligned)
  • Mobile: Action on top (stacked), Cancel below, both full-width

Sizes

SizeUse ForTailwind
defaultStandard dialogssm:max-w-lg (512px)
smCompact confirmationsmax-w-xs (320px)

Use the size prop on AlertDialogContent:

<AlertDialogContent size="sm">...</AlertDialogContent>

The sm size centers header text and uses a 2-column grid layout in the footer.


Styling

Typography

ElementFontSizeWeightLine HeightColor
Titlefont-sans (Adelle Sans)text-lg (18px)font-semibold (Bold)leading-7 (28px)text-foreground
Descriptionfont-sans (Adelle Sans)text-sm (14px)font-normal (Regular)leading-5 (20px)text-muted-foreground

Spacing

ElementPropertyValue
HeaderTitle-to-description gapgap-2 (8px)
FooterButton gapgap-2 (8px)
ContentHeader-to-footer gapComponent spacing (typically gap-4 or gap-6)

Buttons

ButtonVariantHeightPaddingText
Canceloutlineh-9 (36px)px-4 (16px)text-primary
Actiondefaulth-9 (36px)px-4 (16px)text-primary-foreground

Both buttons use:

  • Border radius: rounded-md (8px)
  • Shadow: shadow-xs (0px 1px 2px rgba(0,0,0,0.05))
  • Font: Adelle Sans Semibold, 14px

States

StateImplementation
ClosedNot visible (default)
OpenCentered dialog with dark overlay backdrop

Button states: Cancel and Action buttons follow standard Button component states.


Common Patterns

Basic Confirmation

<AlertDialog>
  <AlertDialogTrigger asChild>
    <Button variant="destructive">Delete Account</Button>
  </AlertDialogTrigger>
  <AlertDialogContent>
    <AlertDialogHeader>
      <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
      <AlertDialogDescription>
        This action cannot be undone. This will permanently delete your account.
      </AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>Cancel</AlertDialogCancel>
      <AlertDialogAction>Continue</AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>

Controlled Dialog

const [open, setOpen] = useState(false)

<AlertDialog open={open} onOpenChange={setOpen}>
  <AlertDialogContent>
    <AlertDialogHeader>
      <AlertDialogTitle>Discard changes?</AlertDialogTitle>
      <AlertDialogDescription>
        You have unsaved changes. Are you sure you want to leave?
      </AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>Keep Editing</AlertDialogCancel>
      <AlertDialogAction onClick={handleDiscard}>Discard</AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>

Destructive Action Button

<AlertDialogAction variant="destructive">
  Delete
</AlertDialogAction>

With Media Icon

<AlertDialogContent>
  <AlertDialogHeader>
    <AlertDialogMedia>
      <TriangleAlert className="size-8 text-destructive" />
    </AlertDialogMedia>
    <AlertDialogTitle>Delete this item?</AlertDialogTitle>
    <AlertDialogDescription>
      This will permanently remove the item.
    </AlertDialogDescription>
  </AlertDialogHeader>
  <AlertDialogFooter>
    <AlertDialogCancel>Cancel</AlertDialogCancel>
    <AlertDialogAction variant="destructive">Delete</AlertDialogAction>
  </AlertDialogFooter>
</AlertDialogContent>

Compact Size

<AlertDialogContent size="sm">
  <AlertDialogHeader>
    <AlertDialogTitle>Discard draft?</AlertDialogTitle>
    <AlertDialogDescription>
      Your unsaved changes will be lost.
    </AlertDialogDescription>
  </AlertDialogHeader>
  <AlertDialogFooter>
    <AlertDialogCancel>Cancel</AlertDialogCancel>
    <AlertDialogAction>Discard</AlertDialogAction>
  </AlertDialogFooter>
</AlertDialogContent>

Component Structure

{/* Desktop (md+) */}
<div className="max-w-lg">
  
  {/* AlertDialogHeader */}
  <div className="flex flex-col gap-2 text-left">
    <h2 className="text-lg font-semibold leading-7 text-foreground">
      {title}
    </h2>
    <p className="text-sm font-normal leading-5 text-muted-foreground">
      {description}
    </p>
  </div>
  
  {/* AlertDialogFooter */}
  <div className="flex gap-2 items-center justify-end">
    <Button variant="outline" className="h-9 px-4">Cancel</Button>
    <Button className="h-9 px-4">Continue</Button>
  </div>
  
</div>

{/* Mobile (sm) */}
<div className="max-w-lg">
  
  {/* AlertDialogHeader */}
  <div className="flex flex-col gap-2 text-center">
    <h2 className="text-lg font-semibold leading-7 text-foreground">
      {title}
    </h2>
    <p className="text-sm font-normal leading-5 text-muted-foreground">
      {description}
    </p>
  </div>
  
  {/* AlertDialogFooter - stacked, action first */}
  <div className="flex flex-col gap-2 w-full">
    <Button className="h-9 px-4 w-full">Continue</Button>
    <Button variant="outline" className="h-9 px-4 w-full">Cancel</Button>
  </div>
  
</div>

Stacked alert dialogs

AlertDialogContent renders its own backdrop. className styles the panel; overlayClassName styles that backdrop.

You only need it when an alert dialog opens over an already-open dialog. Both layers sit at z-50, which is right on its own — but raising only the panel leaves the alert's backdrop painting underneath the dialog it was opened from. Raise them as a pair:

// the dialog underneath
<DialogContent className="z-[900]" overlayClassName="z-[890]">

// the alert dialog opened on top of it
<AlertDialogContent className="z-[1010]" overlayClassName="z-[1000]">

Keep each backdrop just below its own panel, and both above the pair beneath. Passing overlayClassName merges with the backdrop's own classes rather than replacing them, so the scrim colour and the enter/exit animation survive.

DialogContent takes the same prop and follows the same rule — see Dialog → Stacked dialogs.


Accessibility

  • Focus trapped within dialog when open
  • Escape key triggers cancel action
  • Initial focus goes to Cancel button (safest action)
  • Screen readers announce as "alertdialog" role
  • Title/description linked via aria-labelledby/aria-describedby

Gotchas

ProblemSolution
Click outside doesn't closeBy design - requires explicit button action
Buttons stack wrong on mobileUse flex-col sm:flex-row sm:justify-end on footer
Need async action before closeUse controlled open state with onOpenChange
Action should look dangerousUse variant="destructive" on AlertDialogAction
Mobile text not centeredAdd text-center to header on mobile breakpoint
Button order reversed on mobileAction button should be first (on top) on mobile

See Also


Figma Reference

View in Figma


Last updated: February 23, 2026