Dropdown Menu
role="menu" on a popover="auto" element. Outside-click and Escape dismissal come free from the popover. Arrow-key roving between items is the one part of the WAI-ARIA menu pattern that has to be wired up by hand, there's no native menu element yet. A morph variant further down grows the panel out of its own trigger instead of fading in place.
Usage
import { Button, DropdownMenu, MenuChevron, MenuItem, MenuSeparator } from "@kernelui-lib/react";
function ActionsMenu() {
return (
<DropdownMenu
align="end"
offset={4}
className="my-menu"
render={<Button variant="secondary" iconEnd={<MenuChevron />}>Actions</Button>}
>
<MenuItem onSelect={() => {}}>Edit</MenuItem>
<MenuItem render={<a href="/docs" />}>Documentation</MenuItem>
<MenuSeparator />
<MenuItem destructive onSelect={() => {}}>
Delete
</MenuItem>
</DropdownMenu>
);
}Playground
Toggle every adjustable prop and watch the component — and the code — update live.
Same component — Kernel look stripped. See Platforms.
Usage
<DropdownMenu render={<Button variant="secondary" iconEnd={<MenuChevron />}>Actions</Button>}>
<MenuItem onSelect={() => {}}>Edit</MenuItem>
<MenuItem onSelect={() => {}}>Duplicate</MenuItem>
<MenuSeparator />
<MenuItem destructive onSelect={() => {}}>
Delete
</MenuItem>
</DropdownMenu><kernel-dropdown-menu>
<kernel-button slot="trigger" variant="secondary">
Actions
<svg class="kernel-menu-chevron" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" /></svg>
</kernel-button>
<kernel-menu-item>Edit</kernel-menu-item>
<kernel-menu-item>Duplicate</kernel-menu-item>
<kernel-menu-separator></kernel-menu-separator>
<kernel-menu-item destructive>Delete</kernel-menu-item>
</kernel-dropdown-menu>Props
| Component | Prop | Type |
|---|---|---|
DropdownMenu | render | the trigger (required) |
DropdownMenu | renderContent | RenderProp — replace the popup |
DropdownMenu | className | string | (state) => string |
DropdownMenu | placement | "top" | "bottom" | "left" | "right" |
DropdownMenu | align | "start" | "center" | "end" |
DropdownMenu | offset | number (px) |
MenuItem | render | RenderProp — e.g. <a> / router Link |
MenuItem | className | string | (state) => string |
MenuItem | onSelect | () => void |
MenuItem | destructive | boolean |
MenuChevron | — | no props — drop into a trigger's iconEnd; rotates via aria-expanded |
DropdownMenuMorph | render | the trigger (required) — same shape as DropdownMenu's |
DropdownMenuMorph | renderContent | RenderProp — replace the popup |
DropdownMenuMorph | className | string | (state) => string |
DropdownMenuMorph | placement | "top" | "bottom" | "left" | "right" |
DropdownMenuMorph | align | "start" | "center" | "end" |
DropdownMenuMorph | offset | number (px) |
Morph variant
DropdownMenuMorph is the same menu — it sharesMenuItem/MenuSeparator directly — with a different opening move: the panel's own box grows out of the trigger's exact footprint (width, height, and corner radius all animate) instead of scaling and fading in place.
That one visual difference forces a mechanical one: a popover's hide can't be deferred, so there's no way to let the shrink-back- to-the-trigger animation finish before the panel actually disappears while staying popover="auto". This variant is popover="manual" instead, with its own outside- click and Escape dismissal wired up by hand — the same tradeoffContextMenu and Combobox already make for their own manual popovers. Selecting an item still plays the full shrink animation before the menu closes.
The trigger's own icon rotation/cross-fade (the + that becomes a × below) is authored in the demo, not baked into the component — same convention as MenuChevron: what renders is always visible at the call site.
Usage
import { DropdownMenuMorph, MenuItem, MenuSeparator } from "@kernelui-lib/react";
<DropdownMenuMorph render={<PlusTrigger />}>
<MenuItem onSelect={() => {}}>New file</MenuItem>
<MenuItem onSelect={() => {}}>New folder</MenuItem>
<MenuSeparator />
<MenuItem onSelect={() => {}}>Upload</MenuItem>
</DropdownMenuMorph>Accessibility
Opening moves focus to the first item. Arrow Up/Down, Home, and End move between items. Selecting an item, or pressing Escape, closes the menu. Link items keep native navigation, modifier-clicks, and new-tab behaviour — selection closes the menu withoutpreventDefault.