1.0.0 • Published 4 months ago
@darksnow-ui/dialog v1.0.0
@darksnow-ui/dialog
A modal dialog component built on Radix UI with smooth animations and accessibility features.
Installation
npm install @darksnow-ui/dialog @radix-ui/react-dialog
# or
yarn add @darksnow-ui/dialog @radix-ui/react-dialog
# or
pnpm add @darksnow-ui/dialog @radix-ui/react-dialog
Usage
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@darksnow-ui/dialog"
import { Button } from "@darksnow-ui/button"
export function Example() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Edit Profile</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>
Make changes to your profile here. Click save when you're done.
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="name" className="text-right">
Name
</Label>
<Input id="name" value="Pedro Duarte" className="col-span-3" />
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="username" className="text-right">
Username
</Label>
<Input id="username" value="@peduarte" className="col-span-3" />
</div>
</div>
<DialogFooter>
<Button type="submit">Save changes</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
Components
Dialog
The root container component.
Prop | Type | Default | Description |
---|---|---|---|
open | boolean | - | Controlled open state |
defaultOpen | boolean | false | Default open state |
onOpenChange | (open: boolean) => void | - | Callback when open state changes |
modal | boolean | true | Whether the dialog is modal |
DialogTrigger
Button that opens the dialog.
Prop | Type | Default | Description |
---|---|---|---|
asChild | boolean | false | Render as child element |
children | ReactNode | - | Trigger content |
DialogContent
The modal content container.
Prop | Type | Default | Description |
---|---|---|---|
className | string | - | Additional CSS classes |
children | ReactNode | - | Dialog content |
DialogHeader
Container for title and description.
Prop | Type | Default | Description |
---|---|---|---|
className | string | - | Additional CSS classes |
children | ReactNode | - | Header content |
DialogTitle
Dialog title with proper semantics.
Prop | Type | Default | Description |
---|---|---|---|
className | string | - | Additional CSS classes |
children | ReactNode | - | Title content |
DialogDescription
Optional description text.
Prop | Type | Default | Description |
---|---|---|---|
className | string | - | Additional CSS classes |
children | ReactNode | - | Description content |
DialogFooter
Container for action buttons.
Prop | Type | Default | Description |
---|---|---|---|
className | string | - | Additional CSS classes |
children | ReactNode | - | Footer content |
DialogClose
Button that closes the dialog.
Prop | Type | Default | Description |
---|---|---|---|
asChild | boolean | false | Render as child element |
children | ReactNode | - | Close button content |
Examples
Basic Dialog
import { Dialog, DialogContent, DialogTrigger } from "@darksnow-ui/dialog"
import { Button } from "@darksnow-ui/button"
<Dialog>
<DialogTrigger asChild>
<Button>Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<p>This is a basic dialog.</p>
</DialogContent>
</Dialog>
Confirmation Dialog
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@darksnow-ui/dialog"
import { Button } from "@darksnow-ui/button"
<Dialog>
<DialogTrigger asChild>
<Button variant="destructive">Delete Account</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you absolutely sure?</DialogTitle>
<DialogDescription>
This action cannot be undone. This will permanently delete your
account and remove your data from our servers.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="outline">Cancel</Button>
<Button variant="destructive">Delete</Button>
</DialogFooter>
</DialogContent>
</Dialog>
Form Dialog
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@darksnow-ui/dialog"
import { Button } from "@darksnow-ui/button"
import { Input } from "@darksnow-ui/input"
import { Label } from "@darksnow-ui/label"
<Dialog>
<DialogTrigger asChild>
<Button>Edit Profile</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>
Make changes to your profile here. Click save when you're done.
</DialogDescription>
</DialogHeader>
<form className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="name" className="text-right">
Name
</Label>
<Input
id="name"
defaultValue="Pedro Duarte"
className="col-span-3"
/>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="username" className="text-right">
Username
</Label>
<Input
id="username"
defaultValue="@peduarte"
className="col-span-3"
/>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="email" className="text-right">
Email
</Label>
<Input
id="email"
type="email"
defaultValue="pedro@example.com"
className="col-span-3"
/>
</div>
</form>
<DialogFooter>
<Button type="submit">Save changes</Button>
</DialogFooter>
</DialogContent>
</Dialog>
Controlled Dialog
const [open, setOpen] = useState(false)
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button>Open Controlled Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Controlled Dialog</DialogTitle>
<DialogDescription>
This dialog's open state is controlled by React state.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button onClick={() => setOpen(false)}>Close</Button>
</DialogFooter>
</DialogContent>
</Dialog>
Alert Dialog
import { AlertTriangle } from "lucide-react"
<Dialog>
<DialogTrigger asChild>
<Button variant="destructive">Delete</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<div className="flex items-center gap-2">
<AlertTriangle className="h-5 w-5 text-theme-danger" />
<DialogTitle>Delete confirmation</DialogTitle>
</div>
<DialogDescription>
Are you sure you want to delete this item? This action cannot be undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="outline">Cancel</Button>
<Button variant="destructive">Delete</Button>
</DialogFooter>
</DialogContent>
</Dialog>
Large Content Dialog
<Dialog>
<DialogTrigger asChild>
<Button>View Details</Button>
</DialogTrigger>
<DialogContent className="max-w-4xl max-h-[80vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>Project Details</DialogTitle>
<DialogDescription>
Complete information about this project.
</DialogDescription>
</DialogHeader>
<div className="space-y-6">
<div>
<h3 className="text-lg font-semibold mb-2">Overview</h3>
<p className="text-theme-content-muted">
Lorem ipsum dolor sit amet, consectetur adipiscing elit...
</p>
</div>
<div>
<h3 className="text-lg font-semibold mb-2">Team Members</h3>
<div className="space-y-2">
{/* Team member list */}
</div>
</div>
<div>
<h3 className="text-lg font-semibold mb-2">Timeline</h3>
<div className="space-y-2">
{/* Timeline items */}
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline">Close</Button>
<Button>Edit Project</Button>
</DialogFooter>
</DialogContent>
</Dialog>
Custom Close Button
import { DialogClose } from "@darksnow-ui/dialog"
import { X } from "lucide-react"
<Dialog>
<DialogTrigger asChild>
<Button>Open Dialog</Button>
</DialogTrigger>
<DialogContent className="relative">
<DialogClose asChild>
<Button
variant="ghost"
size="icon"
className="absolute right-4 top-4"
>
<X className="h-4 w-4" />
</Button>
</DialogClose>
<DialogHeader>
<DialogTitle>Custom Close Button</DialogTitle>
</DialogHeader>
<p>This dialog has a custom close button.</p>
</DialogContent>
</Dialog>
Multi-step Dialog
const [step, setStep] = useState(1)
<Dialog>
<DialogTrigger asChild>
<Button>Setup Wizard</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Setup Wizard - Step {step} of 3</DialogTitle>
<DialogDescription>
{step === 1 && "Enter your basic information"}
{step === 2 && "Configure your preferences"}
{step === 3 && "Review and confirm"}
</DialogDescription>
</DialogHeader>
<div className="py-4">
{step === 1 && (
<div className="space-y-4">
<Input placeholder="Full name" />
<Input placeholder="Email" type="email" />
</div>
)}
{step === 2 && (
<div className="space-y-4">
<div className="flex items-center space-x-2">
<input type="checkbox" id="notifications" />
<label htmlFor="notifications">Email notifications</label>
</div>
<div className="flex items-center space-x-2">
<input type="checkbox" id="newsletter" />
<label htmlFor="newsletter">Newsletter subscription</label>
</div>
</div>
)}
{step === 3 && (
<div>
<p>Please review your information and click finish to complete setup.</p>
</div>
)}
</div>
<DialogFooter>
{step > 1 && (
<Button variant="outline" onClick={() => setStep(step - 1)}>
Previous
</Button>
)}
{step < 3 ? (
<Button onClick={() => setStep(step + 1)}>
Next
</Button>
) : (
<Button>
Finish
</Button>
)}
</DialogFooter>
</DialogContent>
</Dialog>
Styling
The dialog components use these CSS classes:
DialogOverlay
:fixed inset-0 z-50 bg-theme-shadow/80
DialogContent
:fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-theme-mark-light bg-theme-surface p-6 shadow-theme-xl
DialogHeader
:flex flex-col space-y-1.5 text-center sm:text-left
DialogTitle
:text-lg font-semibold leading-none tracking-tight
DialogDescription
:text-sm text-theme-content-muted
DialogFooter
:flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2
Accessibility
- Full keyboard navigation support
- Focus trap within the dialog
- Escape key closes the dialog
- Proper ARIA attributes for screen readers
- Focus returns to trigger element when closed
- Supports both modal and non-modal dialogs
Theming
The dialog uses CSS custom properties for theming:
--theme-shadow
- Overlay background color--theme-mark-light
- Border color--theme-surface
- Dialog background--theme-content-muted
- Description text color
Technical Details
- Built on Radix UI Dialog primitive
- Includes smooth enter/exit animations
- Portal rendering for proper z-index management
- TypeScript support with proper type inference
- Supports both controlled and uncontrolled usage
License
MIT © DarkSnow UI
1.0.0
4 months ago