Dialog
A modal plate at the highest float elevation; the page physically recesses behind it while focus is trapped.
@emboss/dialog
Installation
pnpm dlx shadcn@latest add @emboss/dialogUsage
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
export function Example() {
return (
<Dialog>
<DialogTrigger render={<Button>Open</Button>} />
<DialogContent>
<DialogTitle>Write preset</DialogTitle>
<DialogDescription>This overwrites slot A.</DialogDescription>
<DialogFooter>
<DialogClose render={<Button variant="ghost">Cancel</Button>} />
<Button variant="accent">Write</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}API reference
Dialog / DialogTrigger / DialogContent
Root state, trigger, and the modal plate. DialogTitle and DialogDescription are required for accessible naming; DialogClose closes from inside.
| Prop | Type | Default | Description |
|---|---|---|---|
| open / defaultOpen / onOpenChange | boolean / boolean / (open) => void | — | Controlled and uncontrolled open state. |
| showCloseButton | boolean | true | Whether DialogContent renders the corner close button. |
Keyboard
| Keys | Action |
|---|---|
| Esc | Closes the dialog and returns focus to the trigger. |
| Tab | Cycles focus inside the dialog while open. |
Source
View source — dialog.tsxHide source — dialog.tsx
"use client";
import { Dialog as BaseDialog } from "@base-ui/react/dialog";
import { X } from "lucide-react";
import { cn } from "@/lib/utils";
/**
* A modal plate floating high above a darkened page. Focus is trapped,
* scroll is locked, and Escape or the backdrop dismisses.
*
* @example
* <Dialog>
* <DialogTrigger render={<Button>Open</Button>} />
* <DialogContent>
* <DialogTitle>Write preset</DialogTitle>
* <DialogDescription>This overwrites slot A.</DialogDescription>
* </DialogContent>
* </Dialog>
*/
function Dialog(props: React.ComponentProps<typeof BaseDialog.Root>) {
return <BaseDialog.Root {...props} />;
}
function DialogTrigger(props: React.ComponentProps<typeof BaseDialog.Trigger>) {
return <BaseDialog.Trigger data-slot="dialog-trigger" {...props} />;
}
function DialogContent({
className,
children,
showCloseButton = true,
...props
}: React.ComponentProps<typeof BaseDialog.Popup> & {
/** Hide the built-in corner close button. */
showCloseButton?: boolean;
}) {
return (
<BaseDialog.Portal>
<BaseDialog.Backdrop
data-slot="dialog-backdrop"
className="fixed inset-0 z-50 bg-ink/40 transition-opacity duration-(--duration-press) ease-(--ease-press) data-ending-style:opacity-0 data-starting-style:opacity-0"
/>
<BaseDialog.Popup
data-slot="dialog-content"
className={cn(
"fixed top-1/2 left-1/2 z-50 flex w-[min(92vw,28rem)] -translate-x-1/2 -translate-y-1/2 flex-col gap-4 rounded-lg bg-surface-2 p-6 text-ink shadow-float-2 transition-[opacity,scale] duration-(--duration-press) ease-(--ease-press) data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0",
className,
)}
{...props}
>
{children}
{showCloseButton ? (
<BaseDialog.Close
aria-label="Close"
data-slot="dialog-close-corner"
className="absolute top-4 right-4 inline-flex size-7 cursor-pointer items-center justify-center rounded-sm text-ink-faint focus-ring transition-[box-shadow,color] hover:text-ink hover:shadow-flush"
>
<X className="size-4" aria-hidden />
</BaseDialog.Close>
) : null}
</BaseDialog.Popup>
</BaseDialog.Portal>
);
}
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="dialog-header"
className={cn("flex flex-col gap-1.5", className)}
{...props}
/>
);
}
function DialogTitle({
className,
...props
}: React.ComponentProps<typeof BaseDialog.Title>) {
return (
<BaseDialog.Title
data-slot="dialog-title"
className={cn("text-lg leading-none font-semibold", className)}
{...props}
/>
);
}
function DialogDescription({
className,
...props
}: React.ComponentProps<typeof BaseDialog.Description>) {
return (
<BaseDialog.Description
data-slot="dialog-description"
className={cn("text-sm text-ink-muted", className)}
{...props}
/>
);
}
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="dialog-footer"
className={cn("flex justify-end gap-2", className)}
{...props}
/>
);
}
function DialogClose(props: React.ComponentProps<typeof BaseDialog.Close>) {
return <BaseDialog.Close data-slot="dialog-close" {...props} />;
}
export {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
DialogClose,
};