Skip to content

ReferenceMenus

media-menu

A composable menu component for settings, option selection, and actions

Import

import '@videojs/html/ui/menu';
import '@videojs/html/ui/menu-content';
import '@videojs/html/ui/menu-item';
import '@videojs/html/ui/menu-group';
import '@videojs/html/ui/menu-group-label';
import '@videojs/html/ui/menu-separator';
import '@videojs/html/ui/menu-radio-group';
import '@videojs/html/ui/menu-radio-item';
import '@videojs/html/ui/menu-checkbox-item';
import '@videojs/html/ui/menu-item-indicator';

Anatomy

<media-menu> is the root menu and its one positioned popup. The root and nested <media-menu-content> elements are its direct children and share that popup.

<button type="button" commandfor="settings-menu">Settings</button>

<media-menu id="settings-menu">
  <media-menu-content>
    <!-- Parent menu item that opens a submenu -->
    <media-menu-item commandfor="submenu">
      Open submenu
    </media-menu-item>

    <!-- Other parent menu items -->
    <media-menu-group>
      <media-menu-group-label></media-menu-group-label>
      <media-menu-checkbox-item>
        <media-menu-item-indicator></media-menu-item-indicator>
      </media-menu-checkbox-item>
    </media-menu-group>
    <media-menu-separator></media-menu-separator>
    <media-menu-item></media-menu-item>
  </media-menu-content>

  <!-- Sibling Content opened by the parent item -->
  <media-menu-content id="submenu">
    <media-menu-item>Back</media-menu-item>
    <media-menu-radio-group>
      <media-menu-group-label></media-menu-group-label>
      <media-menu-radio-item>
        <media-menu-item-indicator></media-menu-item-indicator>
      </media-menu-radio-item>
    </media-menu-radio-group>
  </media-menu-content>
</media-menu>

Behavior

Menus open from a trigger and close when you select an item, click outside, move focus away, or press Escape. Root menus use side and align as their preferred placement. When the preferred side overflows the positioning boundary, the menu uses the opposite side if it has more space.

Root menus inside a player container join its popup group. Opening one closes any other root menu or popover that is open in the same container. Submenus remain part of their root menu instead of registering separately.

To create a submenu, add another <media-menu-content> as a sibling, give it an id, and point the parent <media-menu-item> at it with commandfor.

Media option groups share their option state with the enclosing menu: the selected value, and whether the options are disabled, hidden, or available. The menu’s trigger inherits the disabled and hidden state and exposes data-availability, and the menu closes if its options disappear while open. See PlaybackRateRadioGroup, QualityRadioGroup, AudioTrackRadioGroup, and CaptionsRadioGroup.

The generated radio group publishes its state to the <media-menu> that contains it. Add an element with data-part="hint" inside the parent <media-menu-item> and the menu writes the selected label into it:

<media-menu-item commandfor="quality-menu">
  Quality
  <span data-part="hint"></span>
</media-menu-item>

Styling

Use data attributes to style open state, highlighted items, selected radio items, and submenu views:

data-highlighted identifies the current menu item. Its value is "pointer" when pointer movement caused the highlight and an empty string for other highlights, including keyboard navigation and type-ahead search. Use [data-highlighted] to match any highlighted item or [data-highlighted=""] to match highlights that were not caused by pointer movement.

The root <media-menu> receives data-open, data-side, and data-align. Each <media-menu-content> receives menu state such as data-open and data-submenu; a content element with an open logical child receives data-child-open.

Every navigable item receives data-item. Use [data-item] when one rule should target regular, radio, checkbox, and submenu-trigger items together.

media-menu[data-open] {
  opacity: 1;
}

media-menu-content[data-child-open] {
  translate: -100% 0;
}

media-menu-item[data-highlighted] {
  background: rgba(255, 255, 255, 0.16);
  transition: background-color 100ms ease-in-out;
}

media-menu-item[data-highlighted=""] {
  transition-duration: 0ms;
}

media-menu-radio-item[aria-checked="true"] {
  font-weight: 600;
}

Accessibility

Menu content renders with role="menu". Items use menuitem, menuitemradio, or menuitemcheckbox roles. Radio and checkbox items reflect selection with aria-checked.

Keyboard controls:

  • Enter / Space: Select the highlighted item.
  • Arrow Up / Arrow Down: Move between items.
  • Arrow Right: Open a submenu.
  • Arrow Left: Return to the parent menu.
  • Escape: Close the root menu or return from a submenu.

Use <media-menu-group-label> inside grouped choices so the group receives an accessible label.

Examples

Basic usage

<video-player class="video-player">
  <media-container>
    <hlsjs-video
      class="video-media"
      src="https://stream.mux.com/s41JYeqIpBMBzE4OzxDyGR2yrp2hD1CQ6gJN9SlVGDQ.m3u8"
      autoplay
      crossorigin="anonymous"
      muted
      playsinline
      loop
    >
      <track kind="captions" src="/docs/demos/captions-button/captions.vtt" srclang="en" label="English" />
      <track kind="subtitles" src="/docs/demos/captions-button/captions.vtt" srclang="es" label="Spanish" />
    </hlsjs-video>
    <div class="menu-bar">
      <button type="button" commandfor="settings-menu" class="settings-trigger">Settings</button>
      <media-menu id="settings-menu" side="top" align="end" class="menu">
        <media-menu-content class="menu-content">
          <media-menu-item commandfor="quality-menu" class="menu-item">
            <span>Quality</span>
            <span class="menu-value">
              <span data-part="hint"></span>
              <span aria-hidden="true">›</span>
            </span>
          </media-menu-item>
          <media-menu-item commandfor="audio-menu" class="menu-item">
            <span>Audio</span>
            <span class="menu-value">
              <span data-part="hint"></span>
              <span aria-hidden="true">›</span>
            </span>
          </media-menu-item>
          <media-menu-item commandfor="speed-menu" class="menu-item">
            <span>Speed</span>
            <span class="menu-value">
              <span data-part="hint"></span>
              <span aria-hidden="true">›</span>
            </span>
          </media-menu-item>
          <media-menu-item commandfor="captions-menu" class="menu-item">
            <span>Captions</span>
            <span class="menu-value">
              <span data-part="hint"></span>
              <span aria-hidden="true">›</span>
            </span>
          </media-menu-item>
          <media-menu-item class="menu-item">Copy link</media-menu-item>
        </media-menu-content>
        <media-menu-content id="quality-menu" class="menu-panel">
          <media-menu-item class="menu-back">
            <span aria-hidden="true">‹</span>
            Quality
          </media-menu-item>
          <media-quality-radio-group class="menu-group">
            <template>
              <media-menu-radio-item class="menu-item">
                <span>
                  <span data-part="label"></span>
                  <sup data-part="tier" class="menu-tier"></sup>
                </span>
                <span data-part="badge" class="menu-badge"></span>
                <media-menu-item-indicator force-mount class="menu-indicator">✓</media-menu-item-indicator>
              </media-menu-radio-item>
            </template>
          </media-quality-radio-group>
        </media-menu-content>
        <media-menu-content id="audio-menu" class="menu-panel">
          <media-menu-item class="menu-back">
            <span aria-hidden="true">‹</span>
            Audio
          </media-menu-item>
          <media-audio-track-radio-group class="menu-group">
            <template>
              <media-menu-radio-item class="menu-item">
                <span data-part="label"></span>
                <media-menu-item-indicator force-mount class="menu-indicator">✓</media-menu-item-indicator>
              </media-menu-radio-item>
            </template>
          </media-audio-track-radio-group>
        </media-menu-content>
        <media-menu-content id="speed-menu" class="menu-panel">
          <media-menu-item class="menu-back">
            <span aria-hidden="true">‹</span>
            Speed
          </media-menu-item>
          <media-playback-rate-radio-group class="menu-group">
            <template>
              <media-menu-radio-item class="menu-item">
                <span data-part="label"></span>
                <media-menu-item-indicator force-mount class="menu-indicator">✓</media-menu-item-indicator>
              </media-menu-radio-item>
            </template>
          </media-playback-rate-radio-group>
        </media-menu-content>
        <media-menu-content id="captions-menu" class="menu-panel">
          <media-menu-item class="menu-back">
            <span aria-hidden="true">‹</span>
            Captions
          </media-menu-item>
          <media-captions-radio-group class="menu-group">
            <template>
              <media-menu-radio-item class="menu-item">
                <span data-part="label"></span>
                <media-menu-item-indicator force-mount class="menu-indicator">✓</media-menu-item-indicator>
              </media-menu-radio-item>
            </template>
          </media-captions-radio-group>
        </media-menu-content>
      </media-menu>
    </div>
  </media-container>
</video-player>

API Reference

media-menu

Root menu state and positioned popup. Content pages are direct children.

Props

PropTypeDefaultDetails
align'start' | 'center' | 'end''start'
boundary'viewport' | 'container' | string & {}—
closeOnEscape
attribute close-on-escape
booleantrue
closeOnOutsideClick
attribute close-on-outside-click
booleantrue
defaultOpen
attribute default-open
booleanfalse
openbooleanfalse
side'top' | 'bottom' | 'left' | 'right''bottom'

State

State is reflected as data attributes for CSS styling.

PropertyTypeDetails
transitionStartingboolean
transitionEndingboolean
openboolean
status'idle' | 'starting' | 'ending'
side'top' | 'bottom' | 'left' | 'right' |...
align'start' | 'center' | 'end' | undefined
isSubmenuboolean

CSS custom properties

VariableDescription
--media-menu-widthWidth of the active menu panel (px).
--media-menu-heightHeight of the active menu panel (px).
--media-menu-available-widthViewport-constrained max width for the menu (px).
--media-menu-available-heightViewport-constrained max height for the menu (px).

Events

EventDescription
open-changeFired before the menu's open state changes. Cancel the event to prevent the change.

media-menu-checkbox-item

Menu item that toggles checked when activated and leaves the menu open. The element itself takes role="menuitemcheckbox".

Props

PropTypeDefaultDetails
checkedboolean—
disabledboolean—

Data attributes

AttributeDescription
data-itemPresent on all navigable item types: Item, RadioItem, CheckboxItem, and the Trigger when acting as a submenu trigger inside a parent menu. Use [data-item] as a shared selector to target all item types at once.
data-highlightedPresent when the item is highlighted. Set to pointer when pointer movement caused the highlight; otherwise empty.

Events

EventDescription
checked-changeFired when the checked state changes.

media-menu-content

One accessible menu page. Root and nested pages are sibling children of <media-menu>.

Events

EventDescription
open-change

media-menu-group

Groups related menu items; the element itself takes role="group". A <media-menu-group-label> child names the group unless it already has an aria-label or aria-labelledby.

media-menu-group-label

Non-interactive label that names its enclosing <media-menu-group> or <media-menu-radio-group>.

media-menu-item

Menu action; the element itself takes role="menuitem". Activation fires a cancelable select event and then closes the menu, unless commandfor names a nested <media-menu-content> page to open instead.

Props

PropTypeDefaultDetails
disabledboolean—

Data attributes

AttributeDescription
data-itemPresent on all navigable item types: Item, RadioItem, CheckboxItem, and the Trigger when acting as a submenu trigger inside a parent menu. Use [data-item] as a shared selector to target all item types at once.
data-highlightedPresent when the item is highlighted. Set to pointer when pointer movement caused the highlight; otherwise empty.

media-menu-item-indicator

Decorative checked-state mark inside a menu item, hidden from assistive technology. It stays hidden unless checked or force-mount is set; option radio groups set checked on the indicators in the items they generate.

Props

PropTypeDefaultDetails
checkedboolean—
forceMount
attribute force-mount
boolean—

media-menu-radio-group

Group of mutually exclusive <media-menu-radio-item> children; the element itself takes role="group".

Props

PropTypeDefaultDetails
valuestring—

Events

EventDescription
value-changeFired when the selected value changes.

media-menu-radio-item

Menu item that selects its value in the enclosing <media-menu-radio-group> and closes the menu. The element itself takes role="menuitemradio", checked while its value matches the group's.

Props

PropTypeDefaultDetails
disabledboolean—
valuestring—

Data attributes

AttributeDescription
data-itemPresent on all navigable item types: Item, RadioItem, CheckboxItem, and the Trigger when acting as a submenu trigger inside a parent menu. Use [data-item] as a shared selector to target all item types at once.
data-highlightedPresent when the item is highlighted. Set to pointer when pointer movement caused the highlight; otherwise empty.

media-menu-separator

Visual divider between groups of menu items; the element itself takes role="separator".