Menu
Install
Add the package and import the component.
pnpm add @hey-mike/tungstenpnpm add @hey-mike/tungstenimport { Menu } from '@hey-mike/tungsten';import { Menu } from '@hey-mike/tungsten';Preview
Same fixtures used by the visual-regression suite.
Usage
apps/docs/app/snapshots/menu/page.tsx
import { VariantGrid } from '../_components/VariantGrid';
import { EditMenu, ViewMenu } from './_examples';
// Menus render CLOSED here (resting trigger, click-to-open) to match the
// component-gallery convention used by Radix/MUI/Ant/shadcn. This is the
// fixture the docs preview iframe shows. The OPEN state — which visual
// regression must cover (items, checkbox, radio, submenu, destructive) — lives
// at the test-only /snapshots/menu/open route (see ./open/page.tsx).
export default function MenuSnapshot() {
return (
<VariantGrid
title="Menu"
variants={[
{ label: 'ghost trigger', node: <EditMenu /> },
{ label: 'checkbox + radio + sub', node: <ViewMenu /> },
]}
/>
);
}
import { VariantGrid } from '../_components/VariantGrid';
import { EditMenu, ViewMenu } from './_examples';
// Menus render CLOSED here (resting trigger, click-to-open) to match the
// component-gallery convention used by Radix/MUI/Ant/shadcn. This is the
// fixture the docs preview iframe shows. The OPEN state — which visual
// regression must cover (items, checkbox, radio, submenu, destructive) — lives
// at the test-only /snapshots/menu/open route (see ./open/page.tsx).
export default function MenuSnapshot() {
return (
<VariantGrid
title="Menu"
variants={[
{ label: 'ghost trigger', node: <EditMenu /> },
{ label: 'checkbox + radio + sub', node: <ViewMenu /> },
]}
/>
);
}
Props
Surface specific to <Menu />.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | — |
| defaultOpen | boolean | false | — |
| open | boolean | — | — |
| onOpenChange | ((open: boolean) => void) | — | — |
| modal | boolean | — | When false, the menu is non-modal: outside scroll/pointer interaction
stays enabled and sibling content is not hidden from assistive tech.
Omit to keep Radix's default (modal). |
Sub-components
Composition slots re-exported from the same module.
MenuContent
| Prop | Type | Default | Description |
|---|---|---|---|
| align | "start" | "center" | "end" | — | — |
| side | "top" | "right" | "bottom" | "left" | — | — |
| sideOffset | number | 4 | — |
MenuRadioGroup
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | — |
| value | string | — | — |
| onValueChange | ((value: string) => void) | — | — |
MenuSub
| Prop | Type | Default | Description |
|---|---|---|---|
| defaultOpen | boolean | — | — |
| open | boolean | — | — |
| onOpenChange | ((open: boolean) => void) | — | — |
MenuSubContent
| Prop | Type | Default | Description |
|---|---|---|---|
| sideOffset | number | 0 | — |
MenuSeparator
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | — |
MenuLabel
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | — |
| id | string | — | Optional element id. When a MenuLabel is rendered inside a MenuGroup
the group auto-wires its aria-labelledby to this id, so the common case
needs no manual wiring; supply id only to name the label explicitly. |
MenuGroup
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | — |
| aria-labelledby | string | — | IDREF naming the group for assistive tech. If omitted and the group
contains a MenuLabel, the label is auto-assigned a generated id and
this is wired to it automatically. |
MenuTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | — | Render through to the child element (Radix slot) instead of a <button>.
Note: the ref type stays HTMLButtonElement even when the child is another
element (e.g. an <a>) — cast at the call site if you need the exact type. |
MenuItem
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | — |
| disabled | boolean | — | — |
| icon | ReactElement<unknown, string | JSXElementConstructor<any>> | — | — |
| shortcut | string | — | — |
| destructive | boolean | — | — |
| href | string | — | — |
| onClick | (() => void) | — | — |
MenuCheckboxItem
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | — |
| checked* | boolean | — | — |
| onCheckedChange | ((checked: boolean) => void) | — | — |
| disabled | boolean | — | — |
| shortcut | string | — | — |
MenuRadioItem
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | — |
| value* | string | — | — |
| disabled | boolean | — | — |
| shortcut | string | — | — |