Menu

A hand-drawn dropdown menu of actions triggered by a button.

Overview

A Menu (dropdown) presents a list of actions triggered by a button — Edit, Duplicate, Delete. For choosing a persistent value from a list, use Select instead.

Anatomy

A <button> trigger (aria-haspopup="menu") opens a floating role="menu" of role="menuitem"s. Both the trigger and the panel are sketchy boxes (@ghds/sketch-core). Data-driven — pass an items array of { value, label, disabled? }.

Variants & States

State Status Notes
Closed Implemented Default; aria-expanded=false.
Open Implemented Panel shown; focus moves into the items.
Highlighted item Implemented The focused menuitem is highlighted.
Disabled item Implemented Per-item; skipped by keyboard.

Usage

Do

  • Use it for actions on an object, not for form value selection (use Select).
  • Keep labels action-oriented (verbs).
  • Group destructive actions carefully and consider a confirmation for irreversible ones.

Don't

  • Don't overload one menu with many unrelated actions.
  • Don't use a Menu where a couple of visible buttons would be clearer.

Accessibility

Key Action
Enter / Space / ↓ Open the menu and focus the first item.
Arrow Up / Down Move focus between items (wraps).
Home / End Focus the first / last item.
Enter / Space Activate the focused item.
Escape Close and return focus to the trigger.

Content

Action labels (verbs). Keep them short.

Props API

React@ghds/react

PropTypeDefaultDescription
labelstringTrigger button label.
itemsMenuItem[]{ value, label, disabled? } per entry.
onSelect(value: string) => voidFires with the activated item value.
disabledbooleanfalseDisables the whole menu.

Web Components@ghds/web-components

PropTypeDefaultDescription
itemsGhMenuItem[]{ value, label, disabled? } (property).
labelstringTrigger label.
disabledbooleanfalseDisables the whole menu.
select eventCustomEvent<string>Dispatched with the activated value.

React Native@ghds/react-native

PropTypeDefaultDescription
labelstringTrigger button label.
itemsMenuItem[]{ value, label, disabled? } per entry.
onSelect(value: string) => voidFires with the activated item value.
disabledbooleanfalseDisables the whole menu.
testIDstringTest handle for queries.

Code examples

// React — @ghds/react
import { Menu } from '@ghds/react/menu';

<Menu
  label="Actions"
  items={[
    { value: 'edit', label: 'Edit' },
    { value: 'duplicate', label: 'Duplicate' },
    { value: 'archive', label: 'Archive', disabled: true },
    { value: 'delete', label: 'Delete' },
  ]}
  onSelect={(value) => run(value)}
/>;
<!-- Web Components — @ghds/web-components -->
<script type="module">
  import '@ghds/web-components/menu';
  const menu = document.getElementById('actions');
  // items is a property, not an attribute
  menu.items = [
    { value: 'edit', label: 'Edit' },
    { value: 'delete', label: 'Delete' },
  ];
  menu.addEventListener('select', (e) => run(e.detail));
</script>
<gh-menu id="actions" label="Actions"></gh-menu>
// React Native — @ghds/react-native
import { Menu } from '@ghds/react-native/menu';

<Menu
  label="Actions"
  items={[
    { value: 'edit', label: 'Edit' },
    { value: 'delete', label: 'Delete' },
  ]}
  onSelect={(value) => run(value)}
/>;

Live Demo

React @ghds/react

Web Components @ghds/web-components