# Drawer

Drawer is a neobrutalist React component from Cruise UI. A side panel (left for navigation, right for a cart or filters) that becomes a bottom sheet on phones.

Install (with the @cruiseui registry in components.json, see Installation):

```bash
npx shadcn@latest add @cruiseui/drawer
```

Import: `import { CruiseDrawer } from "@/components/cruise/drawer";`

## When to use

Navigation on mobile, a cart, filters or details next to the page.

## When not to

A yes/no confirmation (use a Modal). Short hints (use a Tooltip).

## Example (React)

```tsx
import { ShoppingBag } from "lucide-react";
import { CruiseButton } from "@/components/cruise/button";
import { CruiseDrawer } from "@/components/cruise/drawer";

export default function DrawerExample() {
  return (
    <CruiseDrawer
      side="right"
      sheet
      title="Your cart"
      trigger={
        <CruiseButton variant="secondary">
          <ShoppingBag aria-hidden="true" />
          Open cart
        </CruiseButton>
      }
      footer={<CruiseButton className="w-full">Checkout</CruiseButton>}
    >
      <ul className="flex flex-col gap-4">
        <li className="flex justify-between gap-4 border-b-2 border-ink pb-3">
          <span>Classic Celebration Cake × 1</span>
          <span className="font-display font-bold">₦18,500</span>
        </li>
        <li className="flex justify-between gap-4 border-b-2 border-ink pb-3">
          <span>Cupcake Box (6) × 2</span>
          <span className="font-display font-bold">₦19,000</span>
        </li>
      </ul>
    </CruiseDrawer>
  );
}
```

## HTML

Needs theme.css (https://cruiseui.dev/theme.css).

```html
<button type="button" class="brutal btn btn-secondary" data-drawer-open="cart-drawer" aria-controls="cart-drawer" aria-expanded="false">Cart</button>

<div class="drawer-backdrop" hidden></div>
<aside id="cart-drawer" class="drawer" data-side="right" data-sheet role="dialog" aria-modal="true" aria-labelledby="cart-drawer-title">
  <div class="drawer-head">
    <h2 id="cart-drawer-title">Your cart</h2>
    <button type="button" class="brutal drawer-close" aria-label="Close" data-drawer-close>
      <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="m6 6 12 12M18 6 6 18"/></svg>
    </button>
  </div>
  <div class="drawer-body"><!-- items --></div>
  <div class="drawer-foot"><a class="brutal btn on-primary" href="#checkout">Checkout</a></div>
</aside>
```

## Props: CruiseDrawer

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | `ReactNode` | — | — |
| `trigger?` | `ReactElement<unknown, string \| JSXElementConstructor<any>>` | — | The button that opens it (gets aria-expanded / aria-controls). |
| `children` | `ReactNode` | — | — |
| `footer?` | `ReactNode` | — | Sticky actions at the bottom. |
| `side?` | `"left" \| "right"` | `"right"` | — |
| `sheet?` | `boolean` | `false` | — |
| `open?` | `boolean` | — | — |
| `onOpenChange?` | `((open: boolean) => void)` | — | — |
| `className?` | `string` | — | Extra classes on the panel, e.g. "nepa-mode" to force dark mode. |
| `initialFocus?` | `RefObject<HTMLElement \| null>` | — | What gets focus on open (defaults to the first focusable element). |

## Accessibility

- role="dialog" with aria-modal and a title; focus is trapped inside and returns to the opener.
- Esc, the close button and the backdrop all close it; the page behind doesn't scroll.
