Components
Dialog
Radix Dialog dressed as a frosted-acrylic modal — 20px blur overlay + translucent panel.
A Radix Dialog with a 20px blur + saturate overlay and a translucent white-tint panel. Drop-in API-compatible with the shadcn Dialog.
Installation
npx shadcn add https://acrylic-ui.vercel.app/r/dialog.jsonUsage
"use client"import { Button } from "@/registry/acrylic/button"import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogTrigger,} from "@/registry/acrylic/dialog"export default function DialogDemo() { return ( <Dialog> <DialogTrigger asChild> <Button>Open dialog</Button> </DialogTrigger> <DialogContent className="sm:max-w-[420px]"> <DialogHeader> <DialogTitle>Acrylic Dialog</DialogTitle> <DialogDescription> A 20px frosted overlay blurs the page; the panel is a translucent tint. </DialogDescription> </DialogHeader> </DialogContent> </Dialog> )}Examples
Basic (with a form), no close button, and scrollable content with a sticky footer:
"use client"import { Button } from "@/registry/acrylic/button"import { Input } from "@/registry/acrylic/input"import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, DialogClose,} from "@/registry/acrylic/dialog"// The common shadcn Dialog formats, acrylic-styled: basic-with-form, a dialog// with no close (X) button, and one with scrollable content + a sticky footer.// Dialog action buttons are medium (the kit uses 24px here, vs large in Alert).export default function DialogShowcase() { return ( <div className="flex flex-wrap gap-2"> {/* Basic — header + form + footer actions */} <Dialog> <DialogTrigger asChild> <Button>Edit profile</Button> </DialogTrigger> <DialogContent className="sm:max-w-[420px]"> <DialogHeader> <DialogTitle>Edit profile</DialogTitle> <DialogDescription>Make changes to your profile, then save.</DialogDescription> </DialogHeader> <div className="flex flex-col gap-3 py-1"> <Input placeholder="Name" defaultValue="Jagger" /> <Input placeholder="Username" defaultValue="@jagger" /> </div> <DialogFooter> <DialogClose asChild> <Button variant="neutral">Cancel</Button> </DialogClose> <DialogClose asChild> <Button>Save</Button> </DialogClose> </DialogFooter> </DialogContent> </Dialog> {/* No close button — dismiss only via the footer */} <Dialog> <DialogTrigger asChild> <Button variant="neutral">No close button</Button> </DialogTrigger> <DialogContent showCloseButton={false} className="sm:max-w-[380px]"> <DialogHeader> <DialogTitle>Enable notifications?</DialogTitle> <DialogDescription>There is no X — choose an option below.</DialogDescription> </DialogHeader> <DialogFooter> <DialogClose asChild> <Button variant="neutral">Not now</Button> </DialogClose> <DialogClose asChild> <Button>Enable</Button> </DialogClose> </DialogFooter> </DialogContent> </Dialog> {/* Scrollable content with a sticky header + footer */} <Dialog> <DialogTrigger asChild> <Button variant="ghost">Scrollable</Button> </DialogTrigger> <DialogContent className="sm:max-w-[420px]"> <DialogHeader> <DialogTitle>Terms of Service</DialogTitle> <DialogDescription>Scroll to review; header and footer stay put.</DialogDescription> </DialogHeader> <div className="scrollbar-mac max-h-[40vh] overflow-y-auto pr-1 text-[13px] text-muted-foreground"> {Array.from({ length: 12 }).map((_, i) => ( <p key={i} className="mb-2"> {i + 1}. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p> ))} </div> <DialogFooter> <DialogClose asChild> <Button>Agree</Button> </DialogClose> </DialogFooter> </DialogContent> </Dialog> </div> )}Notes
- Exports
Dialog,DialogTrigger,DialogContent,DialogClose,DialogHeader,DialogFooter,DialogTitle,DialogDescription. - The close (X) is the acrylic Button (
variant="ghost" size="icon"). Hide it with<DialogContent showCloseButton={false}>and dismiss via your ownDialogClosebuttons. - On open, focus stays on the trigger (the X is not auto-focused, matching macOS). Override with
onOpenAutoFocusonDialogContentif you need to focus a field. - The panel is a grid, and its children carry
min-w-0— a long unbreakable string inside (atruncated path/URL, a<pre>, a long word) would otherwise push the grid track pastmax-w-lgand stretch the whole panel, while thetruncatenever actually truncated. With the floor at 0 the panel keeps its width and each child handles its own overflow — sotruncate/overflow-x-autoon your content works as written. - On a transparent (Tauri/Electron vibrancy) window, see the README for the optional
modal-acrylicbody-paint.