Import
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogMedia,
AlertDialogTitle,
AlertDialogTrigger,
} from "@timelycare/helix-ui"
Props
interface AlertDialogProps {
open?: boolean
onOpenChange?: (open: boolean) => void
children: React.ReactNode
}
interface AlertDialogTriggerProps {
asChild?: boolean
children: React.ReactNode
}
interface AlertDialogContentProps {
className?: string
size?: "default" | "sm"
children: React.ReactNode
}
interface AlertDialogHeaderProps {
className?: string
children: React.ReactNode
}
interface AlertDialogFooterProps {
className?: string
children: React.ReactNode
}
interface AlertDialogTitleProps {
className?: string
children: React.ReactNode
}
interface AlertDialogDescriptionProps {
className?: string
children: React.ReactNode
}
interface AlertDialogActionProps {
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link"
size?: "default" | "xs" | "sm" | "lg" | "icon"
className?: string
children: React.ReactNode
}
interface AlertDialogCancelProps {
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link"
size?: "default" | "xs" | "sm" | "lg" | "icon"
className?: string
children: React.ReactNode
}
interface AlertDialogMediaProps {
className?: string
children: React.ReactNode
}
Variants
| Breakpoint | Viewport | Header Alignment | Footer Layout | Button Width | Button Order |
|---|---|---|---|---|---|
md | ≥768px | Left-aligned | Horizontal, right-aligned | Auto | Cancel, Action |
sm | <768px | Center-aligned | Stacked, full-width | 100% | 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
| Size | Use For | Tailwind |
|---|---|---|
default | Standard dialogs | sm:max-w-lg (512px) |
sm | Compact confirmations | max-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
| Element | Font | Size | Weight | Line Height | Color |
|---|---|---|---|---|---|
| Title | font-sans (Adelle Sans) | text-lg (18px) | font-semibold (Bold) | leading-7 (28px) | text-foreground |
| Description | font-sans (Adelle Sans) | text-sm (14px) | font-normal (Regular) | leading-5 (20px) | text-muted-foreground |
Spacing
| Element | Property | Value |
|---|---|---|
| Header | Title-to-description gap | gap-2 (8px) |
| Footer | Button gap | gap-2 (8px) |
| Content | Header-to-footer gap | Component spacing (typically gap-4 or gap-6) |
Buttons
| Button | Variant | Height | Padding | Text |
|---|---|---|---|---|
| Cancel | outline | h-9 (36px) | px-4 (16px) | text-primary |
| Action | default | h-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
| State | Implementation |
|---|---|
| Closed | Not visible (default) |
| Open | Centered 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>
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
| Problem | Solution |
|---|---|
| Click outside doesn't close | By design - requires explicit button action |
| Buttons stack wrong on mobile | Use flex-col sm:flex-row sm:justify-end on footer |
| Need async action before close | Use controlled open state with onOpenChange |
| Action should look dangerous | Use variant="destructive" on AlertDialogAction |
| Mobile text not centered | Add text-center to header on mobile breakpoint |
| Button order reversed on mobile | Action button should be first (on top) on mobile |
See Also
- Accessibility: Dialog Accessibility Requirements - Focus trapping, keyboard behavior, ARIA alertdialog role
- Related Components: Dialog (non-blocking), Alert (inline notice)
- Patterns: Component Match
Figma Reference
Last updated: February 23, 2026