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.
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.
<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.
<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.
<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
| Part | Description |
|---|---|
| Trigger | A button with popovertarget, aria-haspopup="menu" and aria-expanded. A text trigger ends in a chevron; an icon trigger names the record. |
| Menu | A popover with role="menu" and an aria-label, on surface-overlay with shadow-md. |
| Item | A button with role="menuitem", or menuitemcheckbox with a check for a setting. |
| Separator | A hairline with role="separator" between groups of actions. |
| Destructive item | flux-menu__item--danger, last and after a separator. It opens a confirmation rather than acting. |
API
| Name | Kind | Description |
|---|---|---|
flux-menu | class | On the popover, with popover, role="menu" and an aria-label. Requires flux-components.js for placement and arrow keys. |
flux-menu__item | class | A button with role="menuitem", or role="menuitemcheckbox" with aria-checked and a flux-menu__check icon. |
flux-menu__item--danger | class | A destructive action, in text-error. Last, after a separator. |
flux-menu__separator | class | A div with role="separator" between groups. |
aria-disabled="true" | attribute | On an item that is unavailable. It stays focusable and announced, and does nothing when chosen. |
popovertarget | attribute | On the trigger, naming the menu’s id. Add aria-haspopup="menu" and aria-expanded="false". |
States
| State | Treatment |
|---|---|
| Closed | Not rendered. The trigger has aria-expanded="false". |
| Open | Placed below the trigger’s end edge, or above it when there is no room. Focus moves to the first item. |
| Item hover and focus | layer-hover-01 behind the item. |
| Unavailable | text-disabled, still focusable. |
| Checked | menuitemcheckbox with aria-checked="true" shows its check in icon-brand. |
Keyboard
| Keys | Action |
|---|---|
Enter / Space | On 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 / End | Moves to the first or last item. |
Escape | Closes the menu and returns focus to the trigger. |
Tab | Closes 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.