# Drawer

> A panel that slides in from the bottom edge (or any edge), built on vaul, with a drag handle and swipe-to-dismiss.

## Facts

- **Product**: Crew OS
- **Family**: Overlays
- **Global**: `window.DigitalCrew.Drawer`
- **Source**: `digitalcrew-orchestrator: components/ui/drawer.tsx`
- **Import in the app**: `import { Drawer } from "@/components/ui/drawer";`
- **Live preview**: /design/components/drawer/preview.html
- **Page**: /design/components/drawer

## 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`](/design/components/sheet.md) or [`Dialog`](/design/components/dialog.md).

**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.

## API

```ts
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

```html
<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>
```

## More in Overlays

- [AlertDialog](/design/components/alert-dialog.md): A blocking confirmation for irreversible or costly actions; same motion and surface as **Dialog**, no close ×, no click-outside dismissal.
- [Command](/design/components/command.md): A keyboard-first search-and-act list (cmdk); the ⌘K menu and the core of every searchable picker.
- [ConfirmDialogProvider](/design/components/confirm-dialog-provider.md): App-wide confirmations through one hook: wrap the app in `ConfirmDialogProvider`, then call `useConfirm().confirm({...})` anywhere.
- [Dialog](/design/components/dialog.md): A modal for focused tasks that need an answer before going on; opens in 250ms from 96% scale and closes in 150ms (`.t-modal`).
- [DiscardChangesDialog](/design/components/discard-changes-dialog.md): An alert dialog that asks the user to confirm throwing away unsaved edits.
- [DropdownMenu](/design/components/dropdown-menu.md): A menu of actions for one object, opening from a button; 250ms in from 97%, 150ms out to 99%.
- [Popover](/design/components/popover.md): A light panel anchored to its trigger; opens in 250ms from 97% scale (`.t-dropdown`).
- [Sheet](/design/components/sheet.md): A panel that slides in from an edge; opens in 400ms and closes in 350ms on the smooth-out ease (`--panel-*`).
- [Tooltip](/design/components/tooltip.md): A short ink label on hover or focus: `foreground` fill with `background` text, 12px, 150ms in after an 80ms delay, 50ms out (`.t-tt`).
