Skip to content
FrameworkStyle

media-menu

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

Import

import '@videojs/html/ui/menu';

Anatomy

A root menu owns one positioned Popup. Nested Content elements share that Popup with the root Content.

<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 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.

In React, create a submenu by nesting another Menu.Root in the parent Menu.Content; its Menu.Trigger behaves as a parent item and its Menu.Content becomes the active Content. In HTML, 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 can render the current selection beside a submenu trigger. See PlaybackRateRadioGroup, QualityRadioGroup, AudioTrackRadioGroup, and CaptionsRadioGroup.

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 Menu.Popup or <media-menu> receives data-open, data-side, and data-align. Each Content receives menu state such as data-open and data-submenu; a Content 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 Menu.GroupLabel or <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

Props

PropTypeDefaultDetails

State

State is reflected as data attributes for CSS styling.

PropertyTypeDetails

CSS custom properties

VariableDetails

Events

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

media-menu-checkbox-item

A checkbox-style menu item. Renders a <div> with role="menuitemcheckbox".

Props

PropTypeDefaultDetails

Data attributes

AttributeTypeDetails

Events

EventDescription
checked-changeFired when the checked state changes.

media-menu-content

Accessible item and focus scope for exactly one root or nested menu page.

Events

EventDescription
open-change

media-menu-group

Groups related menu items. Renders a <div> with role="group".

media-menu-group-label

Non-interactive label for a group of items. Renders a <div>.

media-menu-item

A single action in the menu. Renders a <div> with role="menuitem".

Props

PropTypeDefaultDetails

Data attributes

AttributeTypeDetails

media-menu-item-indicator

Visual indicator for a checked state. Only renders when checked is true (or forceMount is set).

Props

PropTypeDefaultDetails

media-menu-radio-group

A group of mutually exclusive radio items. Renders a <div> with role="group".

Props

PropTypeDefaultDetails

Events

EventDescription
value-changeFired when the selected value changes.

media-menu-radio-item

A radio-style menu item. Renders a <div> with role="menuitemradio".

Props

PropTypeDefaultDetails

Data attributes

AttributeTypeDetails

media-menu-separator

Visual divider between groups of items. Renders a <div> with role="separator".