HyMenuItem
Usage examples on this page are written for Lit / plain HTML (
<hy-menu-item>). The same component ships asHyMenuItemin@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
Used inside <hy-menu> — it only means something as part of it.
A row inside an hy-menu. Renders a role="menuitem" button (or an <a> when href is set) with optional leading icon, trailing indicator, and shortcut hint.
The item emits select on activation. When href is set, the event fires synchronously before navigation; calling event.preventDefault() on the select event cancels the link follow. This matches native <a> + click semantics and lets consumers gate navigation (e.g. unsaved changes prompts).
Focus is driven by the parent hy-menu via roving tabindex on the shadow button — the item does not manage its own focusedness. That button is the one element carrying the item role and its state (aria-disabled, aria-haspopup, aria-expanded); the host is role="none", so the accessibility tree holds a single item, and the element that has focus is the element that is announced.
When not to use
- An option that is on or off:
hy-menu-item-checkbox. - One option out of a set:
hy-menu-item-radio.
Examples
Items with shortcut hints and a danger action
<hy-menu-item value="cut" label="Cut" shortcut="⌘X"></hy-menu-item>
<hy-menu-item value="copy" label="Copy" shortcut="⌘C"></hy-menu-item>
<hy-menu-item value="paste" label="Paste" shortcut="⌘V" disabled></hy-menu-item>
<hy-menu-item value="delete" label="Delete" variant="danger"></hy-menu-item>Submenus
An hy-menu placed as a direct child makes the row a submenu owner: it draws a trailing chevron-right (mirrored in right-to-left text), carries aria-haspopup="menu" and an aria-expanded that follows the submenu, and a click, Enter or Space opens the submenu instead of firing select (ArrowRight and pointer hover, handled by the parent menu, open it too; hovering another row closes it). The submenu opens beside the row — right-start, left-start in right-to-left text — unless it sets its own placement. The nested menu is moved to the item's submenu slot, outside the row's button, so a click inside the submenu never activates the row. A slotted trailing element replaces the chevron.
Row owning a submenu
<hy-menu-item value="share" label="Share">
<hy-menu>
<hy-menu-item value="email" label="Email"></hy-menu-item>
<hy-menu-item value="link" label="Copy link"></hy-menu-item>
</hy-menu>
</hy-menu-item>Item with a description line
<hy-menu-item value="attach" label="Attach file" description=".pdf, .png, .jpeg or .md, .txt">
<hy-icon slot="leading" name="paperclip" decorative></hy-icon>
</hy-menu-item>Link item with a leading icon
<hy-menu-item value="docs" href="/docs" label="Documentation">
<hy-icon slot="leading" name="information" decorative></hy-icon>
</hy-menu-item>API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
value | value | string | '' | Identifier used in the select event detail. |
label | label | string | '' | Label text. When empty, the default slot is rendered instead. |
description | description | string | '' | A second line under the label, in the subtle ink and caption type — what the item accepts, what it will do. Wraps (the label keeps its ellipsis) and is announced as the item's description, not as part of its name. |
shortcut | shortcut | string | '' | Keyboard shortcut hint (plain string; platform formatting is the consumer's job). |
disabled | disabled | boolean | false | Disables the item — skipped in keyboard navigation, no select emission. |
variant | variant | 'default' | 'danger' | 'default' | Visual variant. danger signals a destructive action. |
size | size | 'small' | 'medium' | 'medium' | Density variant. small matches hy-list size="small" density for overflow menus / compact triggers. Usually set by a parent hy-menu or hy-menu-button rather than per-item. |
href | href | string | '' | When set, renders as an anchor. select still fires before navigation. |
target | target | string | '' | Anchor target — only meaningful when href is set. |
active | active | boolean | false | Whether this item currently owns the tab stop in its menu's roving tabindex. Reflected so the modal focus trap can match hy-menu-item[active] and so consumer CSS can highlight the current item. Managed by the parent hy-menu — do not set manually. |
Events
| Event | Detail | Description |
|---|---|---|
select | — | { value, item, originalEvent } — bubbles to parent menu. Not fired by a submenu owner, whose activation opens its submenu. |
Slots
| Slot | Description |
|---|---|
default | A direct hy-menu child is the row's submenu (the row moves it to an internal slot outside its button; don't set a slot on it). The row's text is label. |
leading | Leading icon or avatar. |
trailing | Trailing indicator (check, badge). The shortcut text takes this spot when no trailing slot is provided; a submenu owner draws its own chevron here. |
CSS Parts
| Part | Description |
|---|---|
base | The interactive button/anchor element. |
prefix | The leading slot container. |
label | The label text container. |
description | The description line under the label. |
suffix | The trailing slot container. |
shortcut | The shortcut hint text. |
submenu-icon | The chevron a submenu owner draws. |
CSS Custom Properties
The documented hooks. Every override hook the component reads — one per token it paints — is listed in @whitespaceux/harmony/theme-api.json, and @whitespaceux/harmony/component-bindings.json (the Harmony MCP get-bindings tool) says which part and state each one paints.
| Property | Default | Description |
|---|---|---|
--hy-menu-item-background-rest | transparent | — |
--hy-menu-item-background-neutral-hover | var(--hy-background-neutral-hover) | — |
--hy-menu-item-background-danger-tint-subtle | var(--hy-background-danger-tint-subtle) | — |
--hy-menu-item-foreground-default-rest | var(--hy-foreground-default-rest) | — |
--hy-menu-item-foreground-subtle-rest | var(--hy-foreground-subtle-rest) | — |
--hy-menu-item-foreground-danger-rest | var(--hy-foreground-danger-rest) | — |
--hy-menu-item-foreground-default-disabled | var(--hy-foreground-default-disabled) | — |
--hy-menu-item-gap-row-list-md-inline | var(--hy-gap-row-list-md-inline) | — |
--hy-menu-item-gap-row-list-sm-inline | var(--hy-gap-row-list-sm-inline) | — |
--hy-menu-item-padding-row-list-md-block | var(--hy-padding-row-list-md-block) | — |
--hy-menu-item-padding-row-list-sm-block | var(--hy-padding-row-list-sm-block) | — |
--hy-menu-item-padding-row-list-md-inline | var(--hy-padding-row-list-md-inline) | — |
--hy-menu-item-padding-row-list-sm-inline | var(--hy-padding-row-list-sm-inline) | — |
--hy-menu-item-radius-row-list-sm | var(--hy-radius-row-list-sm) | — |
Methods
focus()
Imperative focus — called by the parent menu's roving tabindex logic.
setTabIndex()
Sets the internal button/anchor's tabindex AND mirrors the tab-stop state to the host's reflected [active] attribute so the focus trap can see which item is the current tab stop. Called by the parent menu's roving-tabindex controller.