Flux 2.0

Component

Action menu

A short list of actions behind one button, for a record or a toolbar.

Plain-text source, for agents and clipboard use: /components/source/menu.txt

When to use it

Gathers the less frequent actions for one thing — a booking, a row, an export — behind a single trigger, so the frequent action can stay a visible button beside it. It is a popover with role="menu": the platform gives it light dismiss, Escape and focus return, and flux-components.js places it below its trigger and moves through it with the arrow keys. Separators group related actions, and a destructive action sits last, in text-error.

Not this
Not for choosing a value; use a Select. Not for navigation between pages; use links. Not for the one action someone came to take; make that a button. Not for a single action; show it as a button.

Examples

Record actions

Edit is the frequent action, so it stays a button. The rest wait in the menu, with the destructive one last. Send manifest is unavailable until a guide is assigned, so it stays in place, focusable and announced as unavailable.

html
<button class="flux-button flux-button--secondary" type="button">Edit booking</button>
<button class="flux-button flux-button--secondary" type="button" popovertarget="booking-actions" aria-haspopup="menu" aria-expanded="false">More actions<svg class="flux-button__chevron" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button>
<div class="flux-menu" id="booking-actions" popover role="menu" aria-label="Actions for KB-104931">
  <button class="flux-menu__item" type="button" role="menuitem">Duplicate</button>
  <button class="flux-menu__item" type="button" role="menuitem">Send confirmation</button>
  <button class="flux-menu__item" type="button" role="menuitem" aria-disabled="true">Send manifest</button>
  <div class="flux-menu__separator" role="separator"></div>
  <button class="flux-menu__item flux-menu__item--danger" type="button" role="menuitem">Cancel booking</button>
</div>

Icon trigger

In a row or a card header, an icon button opens the menu. Its accessible name says which record the actions belong to.

html
<button class="flux-table__icon-button" type="button" popovertarget="departure-actions" aria-haspopup="menu" aria-expanded="false" aria-label="More actions for DEP-0914"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><circle cx="3.5" cy="8" r="1.25" /><circle cx="8" cy="8" r="1.25" /><circle cx="12.5" cy="8" r="1.25" /></svg></button>
<div class="flux-menu" id="departure-actions" popover role="menu" aria-label="Actions for DEP-0914">
  <button class="flux-menu__item" type="button" role="menuitem">Send manifest</button>
  <button class="flux-menu__item" type="button" role="menuitem">Export passenger list</button>
  <div class="flux-menu__separator" role="separator"></div>
  <button class="flux-menu__item flux-menu__item--danger" type="button" role="menuitem">Cancel departure</button>
</div>

Checkable items

menuitemcheckbox toggles in place and keeps the menu open, so several can be changed at once.

html
<button class="flux-button flux-button--secondary" type="button" popovertarget="calendar-show" aria-haspopup="menu" aria-expanded="false">Show<svg class="flux-button__chevron" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg></button>
<div class="flux-menu" id="calendar-show" popover role="menu" aria-label="Show on the calendar">
  <button class="flux-menu__item" type="button" role="menuitemcheckbox" aria-checked="true"><svg class="flux-menu__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>Departures</button>
  <button class="flux-menu__item" type="button" role="menuitemcheckbox" aria-checked="true"><svg class="flux-menu__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>Supplier deadlines</button>
  <button class="flux-menu__item" type="button" role="menuitemcheckbox" aria-checked="false"><svg class="flux-menu__check" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8L6.5 11.5L13 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>Public holidays</button>
</div>

Anatomy

PartDescription
TriggerA button with popovertarget, aria-haspopup="menu" and aria-expanded. A text trigger ends in a chevron; an icon trigger names the record.
MenuA popover with role="menu" and an aria-label, on surface-overlay with shadow-md.
ItemA button with role="menuitem", or menuitemcheckbox with a check for a setting.
SeparatorA hairline with role="separator" between groups of actions.
Destructive itemflux-menu__item--danger, last and after a separator. It opens a confirmation rather than acting.

API

NameKindDescription
flux-menuclassOn the popover, with popover, role="menu" and an aria-label. Requires flux-components.js for placement and arrow keys.
flux-menu__itemclassA button with role="menuitem", or role="menuitemcheckbox" with aria-checked and a flux-menu__check icon.
flux-menu__item--dangerclassA destructive action, in text-error. Last, after a separator.
flux-menu__separatorclassA div with role="separator" between groups.
aria-disabled="true"attributeOn an item that is unavailable. It stays focusable and announced, and does nothing when chosen.
popovertargetattributeOn the trigger, naming the menu’s id. Add aria-haspopup="menu" and aria-expanded="false".

States

StateTreatment
ClosedNot rendered. The trigger has aria-expanded="false".
OpenPlaced below the trigger’s end edge, or above it when there is no room. Focus moves to the first item.
Item hover and focuslayer-hover-01 behind the item.
Unavailabletext-disabled, still focusable.
Checkedmenuitemcheckbox with aria-checked="true" shows its check in icon-brand.

Keyboard

KeysAction
Enter / SpaceOn the trigger, opens the menu. On an item, chooses it and closes the menu, returning focus to the trigger.
↓ ↑Moves to the next or previous item, wrapping at the ends.
Home / EndMoves to the first or last item.
EscapeCloses the menu and returns focus to the trigger.
TabCloses the menu and moves on.

Accessibility

  • Give the menu an aria-label that names the record, and the icon trigger an accessible name that does the same.
  • Use aria-disabled rather than disabled on an unavailable item, so it is still reached and announced.
  • A destructive item opens a confirmation Modal that names the consequence; it never acts on its own.
  • The menu closes when the page scrolls, so it never floats away from its trigger.

Tokens

The tier-3 and tier-2 tokens this component binds to. Change the token, not the component.

  • --flux-menu-bg | -border | -shadow | -radius | -padding | -min-width
  • --flux-menu-item-padding-x | -item-padding-y | -item-hover | -item-font-size
  • --flux-menu-separator | -item-danger | -offset

Do and don’t

Do

  • Keep the frequent action as a visible button beside the menu.
  • Order items by how often they are used, and group them with separators.
  • Name each item with a verb and its object: "Send manifest".

Don’t

  • Do not put a single action in a menu.
  • Do not nest menus.
  • Do not let a destructive item act without a confirmation.
  • Do not hide an unavailable action. Leave it in place and unavailable, so the menu keeps its shape.