Skip to content

Menu ​

A menu is a list of things to do. That is the whole difference from a ListBox, which holds a choice: picking a menu item fires it and the menu goes away. Most of the time a menu lives inside a Dropdown, which supplies the trigger and the popover around it.

ts
import { Menu, MenuItem, MenuItemIndicator, MenuSection } from "ropav";

Each MenuItem needs an id, which is what the action event carries, and a text-value for typeahead where the label is not plain text. MenuSection groups items; a Separator between sections draws the line.

A Description beside the Label makes the item two lines, with a leading icon and a trailing Kbd staying on the label's own line — wrap the pair yourself if you need a trailing element of another kind.

menu-basic.vue
vue
<script setup lang="ts">
import { Label, Menu, MenuItem, Separator, Surface } from "ropav";
</script>

<template>
  <Surface class="surface">
    <Menu aria-label="File actions">
      <MenuItem id="new" text-value="New file">
        <Label>New file</Label>
      </MenuItem>
      <MenuItem id="copy" text-value="Copy link">
        <Label>Copy link</Label>
      </MenuItem>

      <Separator />

      <MenuItem id="delete" text-value="Delete file" variant="danger">
        <Label>Delete file</Label>
      </MenuItem>
    </Menu>
  </Surface>
</template>

<style scoped>
.surface {
  width: calc(var(--rp-spacing) * 56);
  padding: 0;
}
</style>

variant="danger" on an item is the one place colour does work in a menu — and it still needs the word "Delete" beside it. The Label in the demo is for the type, not the colour.

When a menu holds a choice ​

selection-mode turns items into a checked set, for the view-style and sort-order menus that are really settings. MenuItemIndicator is the tick, and should-close-on-select is worth setting to false there: a menu of checkboxes that closes on the first tick makes the second one a chore.

MenuItemSubmenuIndicator is the arrow on an item that opens a submenu.

Accessibility ​

  • The menu is a role="menu" and each item a role="menuitem", so a reader is told they have entered a menu and how many things are in it.
  • Inside a dropdown the trigger names the menu through aria-labelledby. A menu standing on its own needs aria-label instead.
  • auto-focus decides where focus lands when the menu opens: a selected item wins over the end you asked for, and with neither the menu takes focus itself.
  • Typeahead matches on the item's text. Where the label is an icon and a word, text-value is what makes the item reachable by typing.

API ​

PropTypeDefaultDescription
ariaLabelstring—Names the menu when it has no trigger to point ariaLabelledby at.
ariaLabelledbystring—The id of the element naming the menu, normally its trigger.
autoFocusboolean | FocusStrategy—Where focus lands when the menu appears. A selected item wins over the end asked for, and with neither the menu itself takes focus.
defaultSelectedKeys"all" | Iterable<CollectionKey>—
disabledBehavior"all" | "selection""all"
disabledKeysIterable<CollectionKey>—
disallowEmptySelectionboolean—
idstring—Overrides the menu's id, which a trigger's aria-controls points at.
selectedKeys"all" | Iterable<CollectionKey>—
selectionBehavior"replace" | "toggle""toggle"
selectionMode"none" | "single" | "multiple""none"
shouldCloseOnSelectbooleantrueWhether choosing an item closes the menu.
size"xs" | "sm" | "md" | "lg""md"How tall a row stands, matching a field of the same size.

Slots: default, empty.

EventPayload
actionCollectionKey
close—
selectionChangeCollectionSelection
update:selectedKeysCollectionSelection

Items and their groups are separate components.

PropTypeDefaultDescription
idCollectionKey—The item's key, which selection, disabling and the collection all address it by. Falls back to a generated id, so an item that is only ever acted on still has a stable identity.
isDisabledboolean—Disables the item: it cannot be focused, selected or acted on.
textValuestring—The text typeahead matches against. Read from the item's own content when absent, which skips the description, the indicators and the keyboard shortcut.
variant"danger" | "default""default"Item variant.

The default slot receives:

Slot propType
hasSubmenuboolean
isDisabledboolean
isFocusedboolean
isFocusVisibleboolean
isHoveredboolean
isOpenboolean
isPressedboolean
isSelectedboolean
selectionModeSelectionMode
PropTypeDefaultDescription
type"checkmark" | "dot""checkmark"Which mark the indicator draws. A checkmark reads as "one of several", a dot as "one of these" — the stylesheet does not enforce the pairing, so it is a choice.

The default slot receives:

Slot propType
isSelectedboolean

Slots: default.

PropTypeDefaultDescription
ariaLabelstring—Names the section when it has no Header to point at.
defaultSelectedKeys"all" | Iterable<CollectionKey>—
disallowEmptySelectionboolean—
selectedKeys"all" | Iterable<CollectionKey>—
selectionBehavior"replace" | "toggle""toggle"
selectionMode"none" | "single" | "multiple"—Gives the section a selection of its own, separate from the menu's and from any other section's — one section of text styles, another of alignments. Without this the section is presentational and its items select through the menu.
shouldCloseOnSelectboolean—Whether choosing an item in this section closes the menu. Inherited when absent.

Slots: default.

EventPayload
selectionChangeCollectionSelection
update:selectedKeysCollectionSelection