Overlay Components
Modal dialogs, popovers, and menu components for building interactive UI overlays.
Dialog
Modal dialog component for focused interactions.
Preview
Live Editor
function DialogDemo() { const [open, setOpen] = React.useState(false); return ( <> <button onClick={() => setOpen(true)} style={{ padding: '10px 20px', borderRadius: '8px', background: 'linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%)', color: 'white', border: 'none', cursor: 'pointer', fontWeight: 600, }} > Open Dialog </button> {open && ( <div style={{ position: 'fixed', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(0,0,0,0.4)', zIndex: 50, }}> <div style={{ background: 'white', borderRadius: '12px', padding: '24px', maxWidth: '400px', boxShadow: '0 20px 50px -12px rgba(0,0,0,0.25)', }}> <h3 style={{ margin: 0, marginBottom: '8px' }}>Edit Profile</h3> <p style={{ color: '#64748b', margin: 0, marginBottom: '20px' }}> Make changes to your profile here. </p> <div style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end' }}> <button onClick={() => setOpen(false)} style={{ padding: '8px 16px', borderRadius: '6px', border: '1px solid #e2e8f0', background: 'white', cursor: 'pointer', }} > Cancel </button> <button onClick={() => setOpen(false)} style={{ padding: '8px 16px', borderRadius: '6px', border: 'none', background: '#8b5cf6', color: 'white', cursor: 'pointer', }} > Save </button> </div> </div> </div> )} </> ); }
Result
Loading...
Usage
import {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
} from "@cpod/react";
import { Button } from "@cpod/react";
<Dialog>
<DialogTrigger asChild>
<Button>Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Edit Profile</DialogTitle>
<DialogDescription>
Make changes to your profile here. Click save when done.
</DialogDescription>
</DialogHeader>
<div className="py-4">
{/* Form content */}
</div>
<DialogFooter>
<Button variant="outline">Cancel</Button>
<Button>Save changes</Button>
</DialogFooter>
</DialogContent>
</Dialog>
Sub-components
| Component | Description |
|---|---|
Dialog | Root component (controlled state) |
DialogTrigger | Button that opens the dialog |
DialogContent | Main dialog container |
DialogHeader | Header section |
DialogFooter | Footer section for actions |
DialogTitle | Dialog title |
DialogDescription | Dialog description |
DialogClose | Close button |
AlertDialog
Confirmation dialog for destructive or important actions.
Preview
Live Editor
function AlertDialogDemo() { const [open, setOpen] = React.useState(false); return ( <> <button onClick={() => setOpen(true)} style={{ padding: '10px 20px', borderRadius: '8px', background: '#ef4444', color: 'white', border: 'none', cursor: 'pointer', fontWeight: 600, }} > Delete Account </button> {open && ( <div style={{ position: 'fixed', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(0,0,0,0.6)', zIndex: 50, }}> <div style={{ background: 'white', borderRadius: '12px', padding: '24px', maxWidth: '450px', boxShadow: '0 20px 50px -12px rgba(0,0,0,0.25)', }}> <h3 style={{ margin: 0, marginBottom: '8px' }}>Are you absolutely sure?</h3> <p style={{ color: '#64748b', margin: 0, marginBottom: '20px', lineHeight: 1.5 }}> This action cannot be undone. This will permanently delete your account and remove your data from our servers. </p> <div style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end' }}> <button onClick={() => setOpen(false)} style={{ padding: '8px 16px', borderRadius: '6px', border: '1px solid #e2e8f0', background: 'white', cursor: 'pointer', }} > Cancel </button> <button onClick={() => setOpen(false)} style={{ padding: '8px 16px', borderRadius: '6px', border: 'none', background: '#ef4444', color: 'white', cursor: 'pointer', }} > Delete Account </button> </div> </div> </div> )} </> ); }
Result
Loading...
Usage
import {
AlertDialog,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
} from "@cpod/react";
import { Button } from "@cpod/react";
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive">Delete Account</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete your
account and remove your data from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Delete Account</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
Sheet
Side panel overlay that slides in from the edge.
Preview
Live Editor
function SheetDemo() { const [open, setOpen] = React.useState(false); return ( <> <button onClick={() => setOpen(true)} style={{ padding: '10px 20px', borderRadius: '8px', background: 'linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%)', color: 'white', border: 'none', cursor: 'pointer', fontWeight: 600, }} > Open Sheet </button> {open && ( <div style={{ position: 'fixed', inset: 0, backgroundColor: 'rgba(0,0,0,0.6)', zIndex: 50, }}> <div onClick={() => setOpen(false)} style={{ position: 'absolute', inset: 0, }} /> <div style={{ position: 'absolute', right: 0, top: 0, bottom: 0, width: '350px', background: 'white', padding: '24px', boxShadow: '-20px 0 50px -12px rgba(0,0,0,0.25)', }}> <button onClick={() => setOpen(false)} style={{ position: 'absolute', right: '16px', top: '16px', background: 'none', border: 'none', cursor: 'pointer', fontSize: '20px', color: '#64748b', }} > × </button> <h3 style={{ margin: 0, marginBottom: '8px' }}>Settings</h3> <p style={{ color: '#64748b', margin: 0 }}> Configure your preferences here. </p> </div> </div> )} </> ); }
Result
Loading...
Usage
import {
Sheet,
SheetTrigger,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
} from "@cpod/react";
import { Button } from "@cpod/react";
// Right side (default)
<Sheet>
<SheetTrigger asChild>
<Button>Open Settings</Button>
</SheetTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>Settings</SheetTitle>
<SheetDescription>
Configure your account preferences.
</SheetDescription>
</SheetHeader>
<div className="py-4">
{/* Content */}
</div>
</SheetContent>
</Sheet>
// Left side
<Sheet>
<SheetTrigger asChild>
<Button>Open Navigation</Button>
</SheetTrigger>
<SheetContent side="left">
<SheetTitle>Navigation</SheetTitle>
</SheetContent>
</Sheet>
Props
SheetContent:
| Prop | Type | Default | Description |
|---|---|---|---|
side | top | right | bottom | left | right | Sheet position |
Popover
Floating content panel anchored to a trigger element.
Preview
Live Editor
function PopoverDemo() { const [open, setOpen] = React.useState(false); return ( <div style={{ position: 'relative', display: 'inline-block' }}> <button onClick={() => setOpen(!open)} style={{ padding: '10px 20px', borderRadius: '8px', background: 'linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%)', color: 'white', border: 'none', cursor: 'pointer', fontWeight: 600, }} > Open Popover </button> {open && ( <div style={{ position: 'absolute', top: '100%', left: '50%', transform: 'translateX(-50%)', marginTop: '8px', background: 'white', borderRadius: '8px', padding: '16px', boxShadow: '0 10px 40px -10px rgba(0,0,0,0.2)', border: '1px solid #e2e8f0', width: '250px', zIndex: 50, }}> <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}> <label style={{ fontSize: '14px', fontWeight: 500 }}>Width</label> <input placeholder="100%" style={{ padding: '8px 12px', borderRadius: '6px', border: '1px solid #e2e8f0', fontSize: '14px', }} /> </div> </div> )} </div> ); }
Result
Loading...
Usage
import {
Popover,
PopoverTrigger,
PopoverContent,
} from "@cpod/react";
import { Button, Input, Label } from "@cpod/react";
<Popover>
<PopoverTrigger asChild>
<Button variant="outline">Open Popover</Button>
</PopoverTrigger>
<PopoverContent className="w-80">
<div className="grid gap-4">
<div className="space-y-2">
<h4 className="font-medium">Dimensions</h4>
<p className="text-sm text-muted-foreground">
Set the dimensions for the layer.
</p>
</div>
<div className="grid gap-2">
<div className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="width">Width</Label>
<Input id="width" defaultValue="100%" className="col-span-2" />
</div>
</div>
</div>
</PopoverContent>
</Popover>
Tooltip
Informational popup on hover.
Preview
Live Editor
function TooltipDemo() { const [show, setShow] = React.useState(false); return ( <div style={{ position: 'relative', display: 'inline-block' }}> <button onMouseEnter={() => setShow(true)} onMouseLeave={() => setShow(false)} style={{ padding: '10px 20px', borderRadius: '8px', background: '#1e293b', color: 'white', border: 'none', cursor: 'pointer', fontWeight: 600, }} > Hover me </button> {show && ( <div style={{ position: 'absolute', bottom: '100%', left: '50%', transform: 'translateX(-50%)', marginBottom: '8px', background: '#1e293b', color: 'white', borderRadius: '6px', padding: '6px 12px', fontSize: '12px', whiteSpace: 'nowrap', zIndex: 50, }}> Add to library </div> )} </div> ); }
Result
Loading...
Usage
import {
Tooltip,
TooltipTrigger,
TooltipContent,
TooltipProvider,
} from "@cpod/react";
import { Button } from "@cpod/react";
// Wrap your app with TooltipProvider
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="outline">Hover</Button>
</TooltipTrigger>
<TooltipContent>
<p>Add to library</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
DropdownMenu
Dropdown menu for actions and navigation.
Preview
Live Editor
function DropdownMenuDemo() { const [open, setOpen] = React.useState(false); return ( <div style={{ position: 'relative', display: 'inline-block' }}> <button onClick={() => setOpen(!open)} style={{ padding: '10px 20px', borderRadius: '8px', background: 'white', border: '1px solid #e2e8f0', cursor: 'pointer', fontWeight: 600, display: 'flex', alignItems: 'center', gap: '8px', }} > Open Menu <span style={{ fontSize: '10px' }}>▼</span> </button> {open && ( <div style={{ position: 'absolute', top: '100%', left: 0, marginTop: '4px', background: 'white', borderRadius: '8px', boxShadow: '0 10px 40px -10px rgba(0,0,0,0.2)', border: '1px solid #e2e8f0', minWidth: '160px', zIndex: 50, padding: '4px', }}> {['Profile', 'Settings', 'Billing'].map((item) => ( <div key={item} style={{ padding: '8px 12px', borderRadius: '4px', cursor: 'pointer', fontSize: '14px', }} onMouseEnter={(e) => e.currentTarget.style.background = '#f8fafc'} onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'} > {item} </div> ))} <div style={{ height: '1px', background: '#e2e8f0', margin: '4px 0' }} /> <div style={{ padding: '8px 12px', borderRadius: '4px', cursor: 'pointer', fontSize: '14px', color: '#ef4444', }} onMouseEnter={(e) => e.currentTarget.style.background = '#fef2f2'} onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'} > Log out </div> </div> )} </div> ); }
Result
Loading...
Usage
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuCheckboxItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSub,
DropdownMenuSubTrigger,
DropdownMenuSubContent,
} from "@cpod/react";
import { Button } from "@cpod/react";
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">Open Menu</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Profile</DropdownMenuItem>
<DropdownMenuItem>Billing</DropdownMenuItem>
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem className="text-red-600">
Log out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
ContextMenu
Right-click context menu.
Preview
Live Editor
function ContextMenuDemo() { const [menu, setMenu] = React.useState(null); return ( <div onContextMenu={(e) => { e.preventDefault(); setMenu({ x: e.clientX, y: e.clientY }); }} onClick={() => setMenu(null)} style={{ padding: '60px 40px', background: 'linear-gradient(135deg, rgba(139, 92, 246, 0.1) 0%, rgba(34, 211, 238, 0.1) 100%)', borderRadius: '12px', border: '2px dashed #8b5cf6', textAlign: 'center', color: '#64748b', fontSize: '14px', cursor: 'context-menu', position: 'relative', }} > Right-click here {menu && ( <div style={{ position: 'fixed', top: menu.y, left: menu.x, background: 'white', borderRadius: '8px', boxShadow: '0 10px 40px -10px rgba(0,0,0,0.2)', border: '1px solid #e2e8f0', minWidth: '160px', zIndex: 50, padding: '4px', }} onClick={(e) => e.stopPropagation()} > {['Back', 'Forward', 'Refresh', 'Save As...'].map((item) => ( <div key={item} onClick={() => setMenu(null)} style={{ padding: '8px 12px', borderRadius: '4px', cursor: 'pointer', fontSize: '14px', }} onMouseEnter={(e) => e.currentTarget.style.background = '#f8fafc'} onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'} > {item} </div> ))} </div> )} </div> ); }
Result
Loading...
Usage
import {
ContextMenu,
ContextMenuTrigger,
ContextMenuContent,
ContextMenuItem,
ContextMenuSeparator,
ContextMenuSub,
ContextMenuSubTrigger,
ContextMenuSubContent,
} from "@cpod/react";
<ContextMenu>
<ContextMenuTrigger className="w-full h-40 border-2 border-dashed rounded-lg flex items-center justify-center">
Right-click here
</ContextMenuTrigger>
<ContextMenuContent className="w-64">
<ContextMenuItem>Back</ContextMenuItem>
<ContextMenuItem>Forward</ContextMenuItem>
<ContextMenuItem>Refresh</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuSub>
<ContextMenuSubTrigger>More Tools</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-48">
<ContextMenuItem>Save Page As...</ContextMenuItem>
<ContextMenuItem>Create Shortcut...</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuSub>
</ContextMenuContent>
</ContextMenu>