Skip to content

Menu

An action menu surfaces a list of actions triggered by a button or other control. Use it when a set of related actions does not need to be visible at all times.

Component

Use fd-menu and fd-menu-item together to create an action menu that follows the WAI-ARIA menu-button pattern. The menu renders a role="menu" container inside a native popover="auto" surface, with no legacy non-popover fallback path in production. Each item renders a native <button role="menuitem">.

When to use

  • A group of related actions shares one trigger — "Save as draft," "Save & submit," and "Discard" are logically grouped and do not all need to be visible at the same time.
  • Screen space is constrained — A toolbar or table row has room for one action button but needs to offer secondary actions on demand.
  • The actions are infrequent — Users do not need to see every option at a glance. Hiding low-frequency actions behind a trigger reduces visual noise.

When not to use

  • Don't use a menu for navigation linksfd-menu uses role="menu" and role="menuitem", which signal actions to assistive technology. For navigation, use a <nav> with regular links.
  • Don't use a menu for form controls — Selection from a list of options is a listbox or select pattern, not a menu. Menus are for actions that happen when activated, not for choosing a value.
  • Don't use a menu for a single action — If there is only one action, use a button directly. Menus add interaction cost.
  • Don't nest menus (submenus) — Submenus are not supported in v1 and add significant accessibility and usability complexity. Flatten the action list or use a different pattern.

Examples

Menu overview — compare default actions, icon usage, destructive ordering, and disabled items in one compact preview. Open each menu in Storybook for interaction details. View in Storybook →
Long list behavior — when actions exceed the max-height, the menu scrolls internally instead of expanding indefinitely. View in Storybook →

The second embed is intentional. The long-menu state demonstrates internal scrolling behavior that the overview story cannot communicate clearly in a single compact preview.

  • Label the trigger with what the menu contains so the action list is predictable before opening.
  • Keep destructive actions last and visually distinct.
  • Leave disabled actions visible when discoverability matters, but avoid filling a menu with mostly unavailable items.

Properties

NameTypeDefaultDescription
anchorstring | undefinedundefinedID of the external trigger element that owns aria-expanded and positioning
placementPlacementbottom-startPreferred popup placement relative to the anchor element
openbooleanfalseCurrent open state
labelstring | undefinedundefinedAccessible name for the menu via aria-label
labelledbystring | undefinedundefinedAccessible name source for the menu via aria-labelledby

Slots

NameDescription
(default)One or more fd-menu-item children

Events

NameDetailDescription
fd-menu-open-change{ open: boolean }Fired whenever the menu opens or closes

Compatibility note:

  • fd-menu still fires deprecated fd-open during the compatibility window.
  • New consumer code should listen to fd-menu-open-change.

CSS custom properties

NameDefaultDescription
--fd-menu-border-radiusvar(--fdic-corner-radius-lg, 7px)Menu surface corner radius
--fd-menu-min-width180pxMinimum menu width
--fd-menu-max-width320pxMaximum menu width
--fd-menu-max-height300pxMaximum menu height before internal scrolling

Shadow parts

NameDescription
surfacePopover surface element
menuInternal element with role="menu"

Methods

NameDescription
show()Opens the menu, positions it, focuses the first item, and fires fd-menu-open-change
showLast()Opens the menu and focuses the last item. Use for ArrowUp-to-open behavior.
hide()Closes the menu and fires fd-menu-open-change
toggle()Opens or closes the menu based on the current open state

Best practices

Do

Label the trigger with what the menu contains

"Filing actions" or "Export options" tells the user what to expect before opening the menu.

Don't

Use vague triggers like "More" or "..."

Generic triggers force the user to open the menu to understand what it contains. In regulatory contexts, predictability builds trust.

Do

Put destructive actions last

Placing destructive actions at the end of the menu reduces accidental activation and follows user expectations.

Don't

Mix destructive and non-destructive actions randomly

Interleaving high-risk and low-risk actions increases the chance of mistakes in time-pressured workflows.

Content guidelines

Start item labels with a verb.

Menu items are actions. Lead with what happens when the user activates the item.

Do

Export as PDF

Don't

PDF export

Keep item labels short — 1 to 4 words.

Menu items should be scannable. Move additional context to surrounding UI.

Do

Discard filing

Don't

Discard this filing and all associated data permanently

Accessibility

  • fd-menu renders role="menu" inside a native popover="auto" surface. The menu container requires an accessible name — set label (for aria-label) or labelledby (for aria-labelledby) on fd-menu.

  • Popover is the production model: outside click and browser-managed top-layer behavior come from the Popover API. The component still owns placement math, keyboard behavior, and trigger aria-expanded.

  • fd-menu-item renders a native <button role="menuitem"> inside shadow DOM. The button text provides the accessible name.

  • Keyboard model follows the WAI-ARIA menu-button pattern:

    KeyBehavior
    ArrowDownMove to next item (wraps to first)
    ArrowUpMove to previous item (wraps to last)
    HomeMove to first item
    EndMove to last item
    Enter / SpaceActivate item, close menu, return focus to trigger
    EscapeClose menu, return focus to trigger
    TabClose menu, allow natural focus movement
  • Roving tabindex: Only one item has tabindex="0" at a time; the rest have tabindex="-1". Arrow keys move focus between items.

  • Disabled items remain in the arrow-key rotation so users can discover them, but Enter/Space on a disabled item is a no-op. Disabled items use aria-disabled="true".

  • aria-expanded: When anchor is set on fd-menu, the component manages aria-expanded on the anchor element automatically. The consumer must set aria-haspopup="menu" on the trigger.

  • Focus return: On close via Escape or item activation, focus returns to the trigger. On close via outside click, focus goes to the click target. On close via Tab, focus moves naturally.

  • Forced colors: Menu surface and items use system colors (ButtonBorder, ButtonText, Highlight, HighlightText, GrayText) so the menu remains distinguishable in Windows High Contrast mode.

  • Reduced motion: A media query guard suppresses any future animations under prefers-reduced-motion: reduce.

fd-menu-item contract

fd-menu-item is a supporting embedded primitive. It does not get its own top-level docs page, so its authoring contract lives here.

Purpose and relationship to parent

  • Use fd-menu-item only as a child action primitive inside fd-menu or composed components that adopt fd-menu-item into an internal menu, such as fd-split-button.
  • It represents one actionable row in a menu. It is not a navigation link, option, checkbox item, or radio item in v1.

Supported authored surface

NameTypeDefaultNotes
variant"default" | "destructive""default"Use destructive only for high-risk actions
disabledbooleanfalseKeeps the item discoverable while suppressing activation
default slottextVisible item label
icon-start slotfd-icon or equivalent icon contentOptional leading icon

Authoring constraints

  • Author only short action labels. Start with a verb.
  • Keep destructive items last in the parent menu and mark them with variant="destructive".
  • Do not use fd-menu-item outside a menu context or as a standalone button substitute.
  • Do not treat it as navigation content. Use link patterns for navigation.

Accessibility contract

  • fd-menu-item renders a native <button role="menuitem"> inside shadow DOM.
  • Keyboard movement between items is owned by the parent fd-menu.
  • Disabled items remain discoverable in menu navigation and expose aria-disabled="true".
  • Activation fires fd-menu-item-select for new code and deprecated fd-select during the compatibility window.

Compatibility note:

  • fd-menu-item still fires deprecated fd-select during the compatibility window.
  • New consumer code should listen to fd-menu-item-select.

Usage example

html
<fd-menu label="Account actions" anchor="account-actions">
  <fd-menu-item>Edit profile</fd-menu-item>
  <fd-menu-item disabled>Publish filing</fd-menu-item>
  <fd-menu-item variant="destructive">Delete draft</fd-menu-item>
</fd-menu>

Trigger requirements

The trigger element (the button that opens the menu) must have:

  • aria-haspopup="menu" — set by the consumer
  • aria-expanded — managed automatically by fd-menu when anchor is set
  • Keyboard handling to open the menu (Enter/Space/ArrowDown for first item, ArrowUp for last item) — consumer's responsibility for standalone usage; composed components like fd-split-button handle this internally

Known limitations

  • Standalone usage is advancedfd-menu does not render or manage its trigger. Standalone consumers must wire aria-haspopup, trigger keyboard behavior, and show()/hide() calls manually. The recommended path is composed components like fd-split-button, which handle all wiring internally.
  • No type-ahead — Character search to jump to matching items is deferred from v1.
  • No submenus — Nested/cascading menus are out of scope.
  • No separators or groups — Menu item separators and named groups are deferred.
  • No checkbox or radio itemsmenuitemcheckbox and menuitemradio roles are out of scope.
  • No link itemsfd-menu-item renders an action button, not a navigation link. Navigation-link menus are a separate future primitive.
  • No animation — Open/close transitions are not implemented in v1.