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 it with npx shadcn@latest add @cruiseui/drawer.
Install
Terminal
npx shadcn@latest add @cruiseui/drawerFirst time? Installation shows the one-off components.json setup (the @cruiseui registry) and how to turn the theme on.
Example
Corners
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>
);
}
<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>Needs theme.css. Interactive parts (menus, tabs, drawers) also need the script in the spec for that component.
When to use it
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).
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.