Dropdown menu
Dropdown menu is a neobrutalist React component from Cruise UI. A short list of actions behind one button, with icons, hints and a destructive item. Install it with npx shadcn@latest add @cruiseui/dropdown-menu.
Install
Terminal
npx shadcn@latest add @cruiseui/dropdown-menuFirst time? Installation shows the one-off components.json setup (the @cruiseui registry) and how to turn the theme on.
Example
Corners
"use client";
import { Copy, Pencil, Share2, Trash2 } from "lucide-react";
import { CruiseDropdownMenu } from "@/components/cruise/dropdown-menu";
export default function DropdownMenuExample() {
return (
<CruiseDropdownMenu
label="Order options"
items={[
{ label: "Edit order", icon: <Pencil aria-hidden="true" />, hint: "E" },
{ label: "Duplicate", icon: <Copy aria-hidden="true" />, hint: "D" },
{ label: "Share receipt", icon: <Share2 aria-hidden="true" /> },
"separator",
{ label: "Cancel order", icon: <Trash2 aria-hidden="true" />, danger: true },
]}
/>
);
}
<div class="menu-wrap" data-menu>
<button type="button" class="brutal btn btn-secondary" id="order-btn" aria-haspopup="menu" aria-expanded="false" aria-controls="order-menu">
Order options
<svg class="menu-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg>
</button>
<div class="menu" role="menu" id="order-menu" aria-labelledby="order-btn" hidden>
<div class="menu-item" role="menuitem" tabindex="-1">Edit order<span class="menu-hint">E</span></div>
<div class="menu-item" role="menuitem" tabindex="-1">Share receipt</div>
<div class="menu-sep" role="separator"></div>
<div class="menu-item" role="menuitem" tabindex="-1" data-danger>Cancel order</div>
</div>
</div>
<!-- Custom select: same script, role="listbox" -->
<div class="field">
<span class="field-label" id="area-label">Delivery area</span>
<div class="menu-wrap" data-menu style="display: block">
<button type="button" class="select select-trigger" aria-haspopup="listbox" aria-expanded="false" aria-controls="area-list" aria-labelledby="area-label area-value">
<span class="select-value" id="area-value">Yaba</span>
<svg class="menu-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg>
</button>
<div class="menu" role="listbox" id="area-list" aria-labelledby="area-label" hidden>
<div class="menu-item" role="option" tabindex="-1" aria-selected="false"><svg class="menu-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>Ikeja</div>
<div class="menu-item" role="option" tabindex="-1" aria-selected="true"><svg class="menu-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>Yaba</div>
</div>
</div>
</div>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
A few actions behind one button: edit, share, delete.
When not to
Choosing a value in a form (use a Select).
Props
CruiseDropdownMenu
| Prop | Type | Default | Description |
|---|---|---|---|
label | ReactNode | — | Trigger text. |
items | CruiseMenuEntry[] | — | — |
variant? | CruiseButtonVariant | "secondary" | — |
align? | "start" | "end" | "start" | — |
popupClassName? | string | — | Extra classes on the menu, e.g. "nepa-mode" (it renders in <body>). |
className? | string | — | — |
Accessibility
- role="menu" with menuitems; arrows, Home/End and typing a letter move.
- Esc closes it and returns focus to the trigger; items are 44px tall.