Popover
Click-triggered floating panel for contextual actions and details.
01 Live Demo
02 Code Snippet
src/ui/Popover.tsx
import { Popover } from "@/ui/Popover"; <Popover.Root> <Popover.Trigger>Open profile card</Popover.Trigger> <Popover.Content> <h4 className="text-sm font-semibold">Project Access</h4> <p className="mt-1 text-xs text-slate-500">Invite teammates and set role permissions.</p> <div className="mt-3"> <Popover.Close>Done</Popover.Close> </div> </Popover.Content> </Popover.Root>
03 Copy-Paste (Single File)
Popover.tsx
import { useState, type ReactNode } from "react"; function PopoverRoot({ children }: { children: ReactNode }) { return <div className="relative inline-block">{children}</div>; } function PopoverTrigger({ onClick, children }: { onClick?: () => void; children: ReactNode }) { return <button type="button" onClick={onClick} className="rounded-lg border border-slate-200 px-3 py-2 text-sm">{children}</button>; } function PopoverContent({ open, children }: { open: boolean; children: ReactNode }) { if (!open) return null; return <div className="absolute left-0 top-[calc(100%+8px)] z-50 w-72 rounded-xl border border-slate-200 bg-white p-4 shadow-xl">{children}</div>; } type PopoverCompound = typeof PopoverRoot & { Root: typeof PopoverRoot; Trigger: typeof PopoverTrigger; Content: typeof PopoverContent; }; export const Popover = Object.assign(PopoverRoot, { Root: PopoverRoot, Trigger: PopoverTrigger, Content: PopoverContent }) as PopoverCompound; // usage function Example() { const [open, setOpen] = useState(false); return ( <Popover.Root> <Popover.Trigger onClick={() => setOpen((v) => !v)}>Toggle</Popover.Trigger> <Popover.Content open={open}>Popover content</Popover.Content> </Popover.Root> ); }