Overlays
Drawer
A panel that slides in from the bottom edge (or any edge), built on vaul, with a drag handle and swipe-to-dismiss.
Guidelines#
Use it for short, focused tasks on touch-first layouts: picking a meeting slot, confirming a send, quick filters on a phone. On wide screens prefer Sheet or Dialog.
What you provide: Drawer (vaul root: open/onOpenChange or defaultOpen, direction bottom by default, top, left, right), a DrawerTrigger, and DrawerContent holding DrawerHeader (DrawerTitle, DrawerDescription), your body, and DrawerFooter with the main action first and a DrawerClose. DrawerContent passes Radix dialog props through, such as onOpenAutoFocus.
Anatomy: a bg-black/50 overlay fades in and out. The bottom drawer is full width, bg-background, rounded-t-lg with a top border, at most 80vh tall, and shows a 100 × 8px bg-muted handle, fully rounded, 16px from the top (bottom direction only). Side drawers are 75% wide up to 384px from sm. Header and footer pad 16px; the header stacks with 6px gaps, the footer with 8px and sits at the bottom (mt-auto). Title is font-semibold text-foreground; description text-sm text-muted-foreground.
Don't put long forms or nested scrolling lists in a drawer, or open one drawer from another.
Facts#
| Group | Overlays |
|---|---|
| Product | Crew OS |
| Global | window.DigitalCrew.Drawer |
| Source | digitalcrew-orchestrator: components/ui/drawer.tsx |
| Import in the app | import { Drawer } from "@/components/ui/drawer"; |
API#
TypeScript declarations emitted from components/ui/drawer.tsx (the module also exports Drawer, DrawerTrigger, DrawerClose, DrawerContent, DrawerHeader, DrawerFooter, DrawerTitle, DrawerDescription).
declare function Drawer({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Root>): any;
declare function DrawerTrigger({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Trigger>): any;
declare function DrawerPortal({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Portal>): any;
declare function DrawerClose({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Close>): any;
declare function DrawerOverlay({ className, ...props }: React.ComponentProps<typeof DrawerPrimitive.Overlay>): any;
declare function DrawerContent({ className, children, ...props }: React.ComponentProps<typeof DrawerPrimitive.Content>): any;
declare function DrawerHeader({ className, ...props }: React.ComponentProps<"div">): any;
declare function DrawerFooter({ className, ...props }: React.ComponentProps<"div">): any;
declare function DrawerTitle({ className, ...props }: React.ComponentProps<typeof DrawerPrimitive.Title>): any;
declare function DrawerDescription({ className, ...props }: React.ComponentProps<typeof DrawerPrimitive.Description>): any;
export { Drawer, DrawerPortal, DrawerOverlay, DrawerTrigger, DrawerClose, DrawerContent, DrawerHeader, DrawerFooter, DrawerTitle, DrawerDescription, };Example#
The code of the preview above: plain React.createElement against window.DigitalCrew, with realistic data.
<div id="root" class="p-6"></div>
<script>
(function () {
var h = React.createElement, DC = window.DigitalCrew, I = DC.Icons;
function App() {
return h(DC.Drawer, { defaultOpen: true },
h(DC.DrawerTrigger, { asChild: true }, h(DC.Button, { variant: "outline" }, h(I.Calendar), "Book a meeting")),
h(DC.DrawerContent, { onOpenAutoFocus: function (e) { e.preventDefault(); } },
h("div", { className: "mx-auto w-full max-w-md" },
h(DC.DrawerHeader, null,
h(DC.DrawerTitle, null, "Book a meeting with Claire Dubois"),
h(DC.DrawerDescription, null, "CFO at Northwind Traders. Slots are shown in her time zone (Paris).")),
h("div", { className: "grid grid-cols-3 gap-2 px-4" },
["Tue 10:00", "Tue 14:30", "Wed 09:00", "Wed 16:00", "Thu 11:00", "Fri 10:30"].map(function (s, i) {
return h(DC.Button, { key: s, variant: i === 1 ? "default" : "outline", size: "sm" }, s);
})),
h(DC.DrawerFooter, null,
h(DC.Button, null, "Send invite for Tue 14:30"),
h(DC.DrawerClose, { asChild: true }, h(DC.Button, { variant: "ghost" }, "Cancel"))))));
}
ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>