Dropdown menu
Presents a compact list of actions. Menu roles and arrow-key navigation are built in.
Preview
In action
When to use
Use it for
- Three or more actions on one object behind a single trigger.
- Separators to group destructive actions at the end.
Not for
- Choosing a value; use Select.
- Navigation links; use NavigationMenu.
Install
Three ways in. They share one source, so the pixels match whichever you pick.
1. Import the package
npm install @noorddev/vlak-react
// once, next to your app's root
import "@noorddev/vlak-react/css";
import { DropdownMenu } from "@noorddev/vlak-react";Precompiled React and one stylesheet. No compiler to configure. Per-component imports work too: @noorddev/vlak-react/components/dropdown-menu.
2. Vendor the source
npx @noorddev/vlak-cli add dropdown-menu
The StyleX leaf lands in components/vlak/ with its dependencies, for your own compiler to own. See StyleX.
3. Through shadcn
npx shadcn add https://vlak.dev/r/dropdown-menu.json
The same registry item, installed by shadcn's CLI.
CSS only
<link rel="stylesheet" href="node_modules/@noorddev/vlak/css/vlak.css" />
No React. Link vlak.css and use the markup and classes below.
React
import { DropdownMenu } from "@noorddev/vlak-react";
<DropdownMenu
label="Actions"
items={[
{ label: "Rename", onSelect: rename },
{ label: "Duplicate", onSelect: duplicate },
{ separator: true },
{ label: "Delete", onSelect: remove },
{ label: "Archive", disabled: true },
]}
/>Props
DropdownMenu
Action menu with menu semantics and keyboard navigation.
| Prop | Type | Default | Description |
|---|---|---|---|
labelrequired | ReactNode | ||
itemsrequired | DropdownMenuItem[] |
Also accepts HTMLAttributes<HTMLDivElement>.
Keyboard
| Keys | Does |
|---|---|
| Arrow down, Enter, Space | Opens the menu on the first item |
| Arrow up | Opens the menu on the last item |
| Arrow down, Arrow up | Moves between items and wraps |
| Home, End | First or last item |
| Type letters | Moves to the next item starting with them |
| Enter, Space | Selects the item and closes |
| Escape | Closes and returns focus to the trigger |
| Tab | Closes and moves on |
Accessibility
- The trigger is a <button> with aria-haspopup="menu", aria-expanded, and aria-controls; label is its name.
- The panel is role="menu" labelled by the trigger; items are <button role="menuitem"> with one roving tab stop.
- Disabled items carry aria-disabled and are skipped; separators are <hr>. A click outside closes without moving focus.
Markup
<div class="rs-menu" role="menu"><button class="rs-menu-item" role="menuitem">Rename</button><button class="rs-menu-item" role="menuitem">Duplicate</button><hr class="rs-menu-sep" /><button class="rs-menu-item" role="menuitem">Delete</button></div>
Classes
.rs-menu.rs-dropdown.rs-menu-item.rs-menu-item-active.rs-menu-item-disabled.rs-menu-sep