Dialog
The question, set large, with two plain answers. The page holds still behind it.
Report an issue with DialogExample
frame
reply
ruled
frame, open
reply, open
ruled, open
Install
Run it in a project that has the shadcn CLI set up. It brings the 0nlyType base and scrollbar along.
npx shadcn@latest add https:// thedirectors.agency/ ui/ r/dialog.jsonUsage
This example also uses Button.
npx shadcn@latest add https:// thedirectors.agency/ ui/ r/ button.jsonimport { Button } from "@/components/ui/button"import {Dialog,DialogActions,DialogClose,DialogContent,DialogDescription,DialogMeta,DialogTitle,DialogTrigger,} from "@/components/ui/dialog"function Frame() {return (<><DialogMeta><span>Delete a note</span><hr /><span>14 attachments</span></DialogMeta><DialogTitle>Delete <span className="ot-yours">Spring notes</span>?</DialogTitle><DialogDescription>The notes and their 14 attachments go for good. This can't be undone.</DialogDescription><DialogActions><DialogClose asChild><Button variant="bracket" data-autofocus>Keep it</Button></DialogClose><DialogClose asChild><Button variant="statement">Delete</Button></DialogClose></DialogActions></>)}// Under reply the answers are bare DialogClose buttons: the dialog sets them in the italic.function Reply() {return (<><DialogMeta><span>Unsaved changes</span><hr /><span>3 edits</span></DialogMeta><DialogTitle>Discard your changes to <span className="ot-yours">Halden</span>?</DialogTitle><DialogDescription>Your last three edits haven't been saved. Discarded, they can't be brought back.</DialogDescription><DialogActions><DialogClose data-autofocus>Keep editing</DialogClose><DialogClose>Discard</DialogClose></DialogActions></>)}function Ruled() {return (<><DialogMeta><span>Archive a note</span><hr /><span>Last opened 12 September</span></DialogMeta><DialogTitle>Archive <span className="ot-yours">Spring notes</span>?</DialogTitle><DialogDescription>It leaves your notes and waits in the archive. Bring it back whenever you like.</DialogDescription><DialogActions><DialogClose asChild><Button variant="bracket" data-autofocus>Keep it</Button></DialogClose><DialogClose asChild><Button variant="statement">Archive</Button></DialogClose></DialogActions></>)}const variants = [{ variant: "frame", open: "Delete Spring notes", Body: Frame, alert: true },{ variant: "reply", open: "Discard changes", Body: Reply, alert: true },{ variant: "ruled", open: "Archive Spring notes", Body: Ruled, alert: false },] as constexport default function Example() {return (<div className="grid justify-items-start gap-x-10 gap-y-7 sm:grid-cols-3">{variants.map(({ variant, open, Body, alert }) => (<div key={variant} className="grid justify-items-start gap-4"><span className="ot-label">{variant}</span><Dialog alert={alert}><DialogTrigger asChild><Button variant="bracket">{open}</Button></DialogTrigger><DialogContent variant={variant}><Body /></DialogContent></Dialog></div>))}</div>)}// Pinned open in place: a plain, non-modal <dialog open>, so the row can show each surface without a click.Contract
- Underneath: native
<dialog>withshowModal(), plus a hook. - Anatomy:
<dialog class="ot-dialog" closedby="any">holding<form method="dialog">, the.ot-dialog-titlequestion,.ot-dialog-body, then.ot-dialog-actionswith a bracket (safe,autofocus) and a statement (the action). - It's set like a poster: corner marks instead of a box (
ot-corners), anot-metarow of facts along the top (.ot-dialog-meta), and the question set large, with the item as yours ("Delete Spring notes?"). The backdrop is paper at 86% with a blur. Opening is one short phrase: the corners open out into place, the meta rule draws from the left, and the question breathes out from weight 500 and width 88%. - Keyboard: focus goes to the safe answer; Escape closes; a click outside closes. Smooth scrolling pauses while it's open.
varianton DialogContent.reply(after "It has to be design."): the question is ours, heavy (800) and narrow (width 72%); the answers are yours, bareDialogClosebuttons set large in the italic. The answer you point at, or that holds focus, inks and takes a full stop in the accent (content: "." / "", so it isn't read out); pointing at one takes the stop from the other, so only one is ever in view. Opening focuses the safe answer, so it opens with its stop.ruled(after "Less is more."): a grid of ink hairlines to the dialog's edges. The meta's two facts go to the top corners; the question is set at--ot-ffin the left cell, each of its lines standing on a hairline that draws across from the start as it opens; the detail sits ragged on the far side, set to the end; the answers take the cells below, the safe one at the start, the action at the far end. Below 40rem the grid folds to one column.- Alert dialog: for what can't be undone,
role="alertdialog" closedby="closerequest"witharia-describedbyon the body. It closes only with Escape or an answer, never a stray click. Any dialog can carry a form (the specimen's rename);[data-close]buttons close it without submitting.
Props
| Prop | Type | Default | What it does |
|---|---|---|---|
open / defaultOpen / onOpenChange | boolean, (open) => void | Controlled or not, like any shadcn dialog. | |
alert | boolean | false | A question that needs an answer: role alertdialog, and pointing outside doesn't close it. Escape still does. |
variant | "frame" | "reply" | "ruled" | "frame" | On DialogContent. frame: corner marks, a row of facts, the question set large. reply: the question heavy and narrow, the answers set large in the italic because the answer is yours; the one you point at, or that has focus, takes a full stop in the accent. Give it bare DialogClose buttons, not Buttons. ruled: a grid of hairlines to the dialog's edges, the facts in the top corners, the question large on ruled lines that draw across as it opens, the detail ragged on the far side and the answers in the cells below. |
DialogMeta | div | The frame row over the question: what this is, a hairline, one fact. | |
data-autofocus | attribute | Put it on the answer that should take focus when the dialog opens. | |
DialogSurface | dialog | The bare native dialog. Sheet and drawer build on it. | |
DialogTrigger / DialogClose asChild | boolean | false | Pass the open or close behaviour to one child control instead of rendering another button. |
Craft
Where its move comes from
| Component | Reference | Move |
|---|---|---|
| Dialog | SHAPES / GRADIENTS frame | Corners and a metadata row |
| Reply dialog | "It has to be design." | Heavy roman question, the answers yours in large italic |
| Ruled dialog | "Less is more." | The question on ruled lines in a grid of hairlines |
How it moves
| Component | Articulation | What moves |
|---|---|---|
| Dialog | Breath out | The corners open out, the rule draws, the question exhales |
| Reply dialog | Spiccato | The answer you point at inks and its accent full stop lands |
| Ruled dialog | Drawn | The rules under the question draw across from the start |