Alert Dialog
A modal layer that requires an explicit action to dismiss — no backdrop click, no close button. This file is the proof that `layer` is a family: it declares only what genuinely differs from `dialog`.
npx shadcn@latest add @fragiola/alert-dialogShow code
"use client";
import type { ReactNode } from "react";
import { Clickable } from "#/atoms/clickable";
import { AlertDialog } from "#/ui/alert-dialog";
// The floor is palette-surface. The dialog content travels through a portal
// but inherits the palette from the owning subtree. The destructive button
// carries palette-danger explicitly — the one chromatic exception for a
// destructive confirm action.
export default function AlertDialogDemo() {
return (
<div className="palette-surface flex flex-col gap-6 rounded-lg border border-palette-line bg-palette-base p-6">
<Row label="destructive confirm">
<AlertDialog.Root>
<AlertDialog.Trigger
render={
<Clickable.Button
variant="outline"
className="palette-danger"
/>
}
>
Delete account
</AlertDialog.Trigger>
<AlertDialog.Portal>
<AlertDialog.Backdrop />
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title>
Are you absolutely sure?
</AlertDialog.Title>
<AlertDialog.Description>
This action cannot be undone. This will
permanently delete your account and remove
your data from our servers.
</AlertDialog.Description>
</AlertDialog.Header>
<AlertDialog.Footer>
<AlertDialog.Close
render={
<Clickable.Button variant="outline" />
}
>
Cancel
</AlertDialog.Close>
<Clickable.Button className="palette-danger">
Delete
</Clickable.Button>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog.Root>
</Row>
<Row label="non-destructive">
<AlertDialog.Root>
<AlertDialog.Trigger
render={<Clickable.Button variant="outline" />}
>
Publish
</AlertDialog.Trigger>
<AlertDialog.Portal>
<AlertDialog.Backdrop />
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title>
Publish now?
</AlertDialog.Title>
<AlertDialog.Description>
Once published, this post will be visible to
everyone.
</AlertDialog.Description>
</AlertDialog.Header>
<AlertDialog.Footer>
<AlertDialog.Close
render={
<Clickable.Button variant="outline" />
}
>
Not now
</AlertDialog.Close>
<Clickable.Button variant="solid">
Publish
</Clickable.Button>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog.Root>
</Row>
</div>
);
}
function Row({ label, children }: { label: string; children: ReactNode }) {
return (
<div className="flex flex-col gap-2">
<span className="text-xs font-mono text-palette-accent/85">
{label}
</span>
<div className="flex flex-wrap items-center gap-4">{children}</div>
</div>
);
}
One file
Installing alert-dialog after dialog writes exactly one file — this one. The layer family and the createLayerParts factory are already on disk. AlertDialog reuses Dialog's Backdrop, Popup, Title, Description, Close and Portal at runtime; the factory injects the namespace so each component inherits its own divergence if Base UI ever splits them.
What differs
Only three things: the primitive namespace, the absence of a close button (showClose={false}), and the action/cancel pair (which is just Clickable.Button composed in the footer, not a new part). A destructive confirmation is className="palette-danger" on the action button — no variant="destructive", no tone prop.
Parts
| Part | Source |
|---|---|
Root / Trigger / Portal | Base UI AlertDialog |
Backdrop / Content / Header / Body / Footer / Title / Description / Close | layer via createLayerParts |
Dialog
A modal layer — a centred panel over a scrim. Shares the `layer` family with alert-dialog and drawer; the only things this component declares for itself are `Root`, `Trigger`, `Portal` and the centring transform.
Drawer
An edge-anchored layer with real swipe and snap-point support. Built on Base UI's `Drawer` primitive, which is why this component exists and why `sheet` does not.