Skip to content

HyMenuButton

Usage examples on this page are written for Lit / plain HTML (<hy-menu-button>). The same component ships as HyMenuButton in @whitespaceux/harmony-react (native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.

A trigger + menu pair implementing the ARIA menu-button pattern. The trigger is any element slotted into trigger; the menu surface is the child hy-menu (rendered in the default slot) or an actions shorthand.

Trigger shapes

  • Slotted trigger — put any element (hy-button, hy-icon-button, chip, avatar, …) into slot="trigger". You own the label, icons, and styling.
  • Default trigger — when label is set and slot="trigger" is empty, the component renders an internal hy-button caret truncate with the label text and a trailing chevron. The truncate flag means long labels clip with an ellipsis instead of overflowing the row, so the default trigger is safe to use as a "value picker" reflecting an unbounded current selection. Slotted triggers don't get this for free — set truncate on your own hy-button if you need it.

Composition contract

Slotted hy-menu-item children always win over the actions array — same precedence as hy-menu-item's own label prop losing to the default slot. When both are set in dev mode, a single console warning fires per element.

ARIA wiring

The trigger receives aria-haspopup="menu", aria-expanded, and an aria-controls pointing at the internally managed menu id. Clicks on the trigger toggle the menu; Escape from the menu returns focus to the trigger (delegated to hy-popover). A host aria-label forwards to the default trigger, naming the control when the visible label is a current value (e.g. a picker showing "solid") rather than what the control does.

Examples

Slotted trigger with a divider and a danger action

html
<hy-menu-button>
  <hy-button slot="trigger">
    Actions
    <hy-icon slot="suffix" name="chevron-down"></hy-icon>
  </hy-button>
  <hy-menu-item value="edit" label="Edit"></hy-menu-item>
  <hy-menu-item value="duplicate" label="Duplicate"></hy-menu-item>
  <hy-menu-item value="archive" label="Archive"></hy-menu-item>
  <hy-divider role="separator"></hy-divider>
  <hy-menu-item value="delete" label="Delete" variant="danger"></hy-menu-item>
</hy-menu-button>

Default trigger rendered from the label prop

javascript
<hy-menu-button label="Sort by" aria-label="Sort order">
<hy-menu-item value="name" label="Name"></hy-menu-item>
<hy-menu-item value="date" label="Date modified"></hy-menu-item>
<hy-menu-item value="size" label="Size"></hy-menu-item>
</hy-menu-button>
<script>
document.querySelector('hy-menu-button').addEventListener('select', (e) => {
console.log(e.detail.value);
});
</script>

API

Properties

PropertyAttributeTypeDefaultDescription
openopenbooleanfalseControlled open state.
placementplacement'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end' | 'right' | 'right-start' | 'right-end''bottom-start'Placement forwarded to the child menu.
sizesize'small' | 'medium''medium'Density variant forwarded to the child hy-menu (and through it to every menu item). Also sizes the default hy-button trigger (when label renders one). Does not re-size a slotted trigger — consumers size their own trigger element.
labellabelstring''Label used by the default hy-button caret trigger. Ignored when slot="trigger" has content.
widthwidthstring''Fixed menu-panel width (any CSS length, e.g. '220px'). Forwarded to the child hy-menu.
minWidthmin-widthstring''Minimum menu-panel width. Forwarded to the child hy-menu.
fullWidthfull-widthbooleanfalseStretch the trigger to fill the host's inline size. Off by design; opt in for a full-width "select-style" dropdown trigger. The slotted (or default truncate) trigger grows to match. Distinct from width / minWidth, which size the dropped menu panel.
actionsMenuAction[][]Shorthand for rendering items from data. When set, items are rendered only if the default slot is empty. Slotted children always win.

Events

EventDetailDescription
selectRelayed from the child menu. Detail: MenuSelectDetail.
changeRelayed from the child menu when a radio group changes. Detail: MenuChangeDetail{ selectedValue, name, reason }.
showRelayed from the child menu (does NOT bubble).
hideRelayed from the child menu (does NOT bubble).

Slots

SlotDescription
triggerThe trigger element (button, icon-button, chip, avatar, …). When empty and label is set, an internal hy-button caret truncate is rendered (long labels clip with an ellipsis).
defaulthy-menu-item / hy-menu-item-checkbox / hy-menu-item-radio / hy-menu-group / hy-divider children.

CSS Parts

PartDescription
trigger-wrapperThe wrapper element around the trigger slot.

Built with Lit. Documented with VitePress.