Skip to main content

Context Menu

JSThis component requires JavaScript.
An on-demand list of options that appears based on the user's interaction with a specific element.

Examples

PositioningScreenshot
Container position: below
Container justify to caret: left
Caret justify to trigger: center
Popover Example 1
Container position: above
Container justify to caret: right
Caret justify to trigger: center
Popover Example 2
Container position: left
Container align to caret: start
Caret align to trigger: end
Popover Example 3
Container position: right
Container align to caret: end
Caret align to trigger: center
Popover Example 4
Container position: left
Container align to caret: center
Caret align to trigger: center
Popover Example 5

Usage

Dos:

  • Use short and precise labels for menu items.
  • Use as few menu items as possible.

Donts:

  • Don’t overwhelm users with expansive lists of menu items. Keep the list length manageable by including fewer than 10–12 items, to avoid choice overload.

Implementation notes

The Context Menu involves 2 main elements:

  1. trigger: typically a button, which is intended to open the container when clicked.
  2. container: the menu itself, which is hidden until activated by the trigger.

To initialize interactive behavior and make the component accessible, we use JavaScript that will identify triggers by querying for the bux-context-menu__trigger class, then looking for each trigger's corresponding container by finding the first element occuring after the trigger in the DOM order that has the bux-context-menu__container class. This allows for some flexibility in your DOM structure, but typically it's easiest to put the container element immediately after its trigger.

The Twig template relies on the JavaScript and CSS portions of the component to fill in several important attributes -- see the Accessibility section for more information.

Keeping the menu open on clicks outside the container

By default, the menu will collapse if the user clicks outside of the container. To override this behavior and keep the menu open until the user clicks the trigger again or presses Escape, you can add the data-stay-open="true" attribute to the trigger element. This can also be passed in as the stay_open variable in the Twig template.

Positioning

By default, the container will open beneath the trigger element, with the caret justified to the trigger's center, and the container justified to the start of the caret. This is powered by an underlying popover system, and you can adjust it with classes on the trigger and container.

First, on the container element, specify:

  • how to position the container relative to the trigger
    • Above: container--position-above-trigger
    • Below: container--position-below-trigger
    • Left of: container--position-left-of-trigger
    • Right of: container--position-right-of-trigger
  • and how to justify or align the container to the caret:
    • If the container is above or below the trigger, the caret will be on the bottom or top of the container, respectively, so you'll justify the container and caret.
      • Left: container--justify-caret-left
      • Center: container--justify-caret-center
      • Right: container--justify-caret-right
    • If the container is left of or right of the trigger, the caret will be on the right or left side of the container, respectively, so you'll align the container and caret.
      • Top/start: container--align-caret-start
      • Center: container--align-caret-center
      • Bottom/end: container--align-caret-end

Then, on the caret element, you can specify how the caret should align or justify to the trigger

  • If the container is above or below the trigger, the caret will be pointing to the top or bottom of the trigger, respectively, so you'll justify the caret and trigger.
    • Left: caret--justify-trigger-left
    • Center: caret--justify-trigger-center
    • Right: caret--justify-trigger-right
  • If the container is right of or left of the trigger, the caret will be pointing to the right or left side of the trigger, respectively, so you'll align the caret and trigger.
    • Top/start: caret--align-trigger-start
    • Center: caret--align-trigger-center
    • Bottom/end: caret--align-trigger-end

Accessibility

Relationships

On page load, the JavaScript for this component will set up the relationship between the trigger by generating a unique DOM ID, assigning it to the container, and setting the trigger's aria-owns to it. On expand and collapse, the trigger gets updated with aria-expanded="true" and aria-expanded="false", respectively.

Hiding when collapsed

When collapsed, the container is hidden visually, from find-in-page behavior, and from accessible technology with inert and display: none. The trigger element should have a descriptive label. For instance, in an icon button, this could be visually hidden text inside the button.

Focus management

When collapsed, the container's items are not focusable due to display: none and inert. When expanded, the JavaScript will set focus to the first item in the list. From there, focus is trapped in the component by keydown listeners, and will move from the last item in the menu back up to the trigger.

Keyboard navigation

  • Tab and ArrowDown: moves focus to the next item in the menu. Will move back to the trigger button if pressed from the last item in the menu.
  • Shift+Tab and ArrowUp: moves focus to the previous item in the menu. Will move to the last item in the menu if pressed from the trigger button.
  • Home: moves focus to the first item in the menu.
  • End: moves focus to the last item in the menu.
  • Escape: closes the menu.
Back to top