Sheet (registry composite)
A shadcn-aligned wrapper around the Sheet primitive — the same edge-anchored, Radix-Dialog-backed slide-over, re-exposed as a flat SheetContent / SheetHeader / SheetFooter / SheetTitle / SheetDescription API with data-slot attributes. Reach for this when you want to own the source and match the shadcn component shape; reach for the package primitive when you want the compound Sheet.Content statics straight from npm.
This is the registry composite. The bundled
@roger-emerson/ploxum-primitivesSheet primitive is documented separately at Sheet.
Import
Sheet is a registry composite — you own the source after installing:
npx ploxum add sheetimport {
Sheet,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
} from "@/components/sheet";Depends on @roger-emerson/ploxum-primitives (re-exposes its Sheet primitive — Radix Dialog over ploxum tokens + motion).
Usage
Trigger-based (uncontrolled) — wrap your own Button with SheetTrigger asChild. Slots are flat (no compound statics):
<Sheet>
<SheetTrigger asChild>
<Button variant="secondary">Edit profile</Button>
</SheetTrigger>
<SheetContent side="right" size={480}>
<SheetHeader>
<SheetTitle>Edit profile</SheetTitle>
<SheetDescription>Make changes and save when you are done.</SheetDescription>
</SheetHeader>
<div className="flex-1 space-y-4 px-4 py-3">{/* form fields */}</div>
<SheetFooter>
<SheetClose asChild>
<Button variant="ghost">Cancel</Button>
</SheetClose>
<SheetClose asChild>
<Button variant="primary">Save changes</Button>
</SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>side re-anchors the panel to any edge; size sets the width (left/right) or height (top/bottom). A left-anchored nav drawer or a bottom-anchored console are the same component:
<SheetContent side="left" size={300}>{/* navigation */}</SheetContent>
<SheetContent side="bottom" size="40vh">{/* log console */}</SheetContent>Controlled — drive open / onOpenChange on the root yourself (e.g. opening a detail panel from a row click):
const [open, setOpen] = useState(false);
<Sheet onOpenChange={setOpen} open={open}>
<SheetContent side="right" size={480}>
<SheetHeader>
<SheetTitle>Incident INC-2041</SheetTitle>
<SheetDescription>Gate 4 — acknowledged 12:04:21</SheetDescription>
</SheetHeader>
{/* detail body */}
</SheetContent>
</Sheet>;Props
Sheet (root) / SheetTrigger / SheetClose
Re-exports of the primitive's Sheet, Sheet.Trigger, and Sheet.Close — Radix Dialog / Dialog.Trigger / Dialog.Close passthroughs. The root takes open / defaultOpen / onOpenChange; use asChild on the trigger and close to wrap your own Button. Any number of SheetClose elements may sit inside the content (e.g. both Cancel and Save in the footer).
SheetContent
| Prop | Type | Default | Description |
|---|---|---|---|
side | "right" | "left" | "top" | "bottom" | "right" | Edge the panel docks to. |
size | number | string | primitive default | Width for left/right, height for top/bottom. Numbers are px; strings (e.g. "40vh") pass through. |
className | string | — | Merged onto the panel surface. |
children | ReactNode | — | Header / body / footer slots. |
SheetHeader / SheetFooter
Thin wrappers over the primitive's Header / Footer (forward all underlying props, set data-slot="sheet-header" / "sheet-footer"). Header is the bordered title block at the top; Footer is the bordered, right-aligned action bar pinned to the bottom.
SheetTitle / SheetDescription
Wrap the primitive's Title / Description (Radix Dialog.Title / Dialog.Description) — they register the dialog's accessible name and description, so include them (or an equivalent aria-label). Carry data-slot attrs and forward all underlying props plus className.
Design rules
- Same motion + scrim contract as the primitive. The panel springs in from the docked edge and degrades to an opacity-only fade under
prefers-reduced-motion; a flat--ploxum-color-background-scrimoverlay sits behind it. Never blur the content behind the scrim — glassmorphism on content surfaces is banned. Don't add secondary entrance flourishes. - Focus is trapped and returns on close. Radix moves focus into the panel on open and restores it to the trigger on close; Esc and overlay click both dismiss.
data-sloteverywhere. Each part stamps adata-slot(sheet-content/-header/-footer/-title/-description) so consumers can target the slots from their own CSS without prop drilling.- Sheet is for edge-anchored panels;
Dialogis for centered modals. Reach for Sheet when the content is a side rail, drawer, or console (detail drawers, navigation, log consoles). Reach forDialogwhen the interaction is a centered, focused decision. - Composite vs primitive is an ownership choice, not a behavior one. Both render the same surface. Use this composite (flat API, shadcn-aligned, source you own) when you're matching the shadcn component shape or porting shadcn blocks; use the package
Sheetprimitive (compoundSheet.Contentstatics, shipped from npm) otherwise.
