Skip to content
FrameworkStyle

media-menu

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

Anatomy

A submenu is a menu inside another menu. The submenu trigger lives in the parent list, and the submenu content is the next view.

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

<media-menu id="settings-menu">
  <media-menu-view>
    <!-- Parent menu item that opens a submenu -->
    <media-menu-item commandfor="submenu">
      <media-menu-item-value></media-menu-item-value>
    </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-view>

  <!-- Submenu content -->
  <media-menu id="submenu">
    <media-menu-back></media-menu-back>
    <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>
</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.

Create a submenu by putting another menu inside the root menu’s content. Its trigger stays in the root list, and its content becomes the next view. In HTML, give the submenu a matching id and point the trigger at it with commandfor. Wrap the root list in Menu.View or <media-menu-view> when the root list and submenus share one animated viewport.

Set type="playback-rate", type="quality", type="audio-track", or type="captions" on a submenu trigger when it represents a media setting. Menu.ItemValue and <media-menu-item-value> read that setting context and display the current value, such as 1.5x, Auto, English, or Off. See PlaybackRateRadioGroup, QualityRadioGroup, AudioTrackRadioGroup, and CaptionsRadioGroup for the generated option groups.

Styling

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

data-highlighted is added to the current menu item. The highlighted item is the item that keyboard selection will act on. It changes when the menu opens, when the pointer moves over another item, when the user presses arrow keys, or when type-ahead search finds a match.

On root menu content, data-side reflects the rendered side and can differ from the preferred side prop after collision handling. Submenus do not have data-side.

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

media-menu-item[data-highlighted] {
  background: rgba(255, 255, 255, 0.16);
}

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-view class="menu-panel">
                    <media-menu-item commandfor="quality-menu" type="quality" class="menu-item">
                        <span>Quality</span>
                        <span class="menu-value">
                            <media-menu-item-value></media-menu-item-value>
                            <span aria-hidden="true"></span>
                        </span>
                    </media-menu-item>
                    <media-menu-item commandfor="audio-menu" type="audio-track" class="menu-item">
                        <span>Audio</span>
                        <span class="menu-value">
                            <media-menu-item-value></media-menu-item-value>
                            <span aria-hidden="true"></span>
                        </span>
                    </media-menu-item>
                    <media-menu-item commandfor="speed-menu" type="playback-rate" class="menu-item">
                        <span>Speed</span>
                        <span class="menu-value">
                            <media-menu-item-value></media-menu-item-value>
                            <span aria-hidden="true"></span>
                        </span>
                    </media-menu-item>
                    <media-menu-item commandfor="captions-menu" type="captions" class="menu-item">
                        <span>Captions</span>
                        <span class="menu-value">
                            <media-menu-item-value></media-menu-item-value>
                            <span aria-hidden="true"></span>
                        </span>
                    </media-menu-item>
                    <media-menu-item class="menu-item">Copy link</media-menu-item>
                </media-menu-view>

                <media-menu id="quality-menu" class="menu-panel">
                    <media-menu-back class="menu-back">
                        <span aria-hidden="true"></span>
                        Quality
                    </media-menu-back>
                    <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>

                <media-menu id="audio-menu" class="menu-panel">
                    <media-menu-back class="menu-back">
                        <span aria-hidden="true"></span>
                        Audio
                    </media-menu-back>
                    <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>

                <media-menu id="speed-menu" class="menu-panel">
                    <media-menu-back class="menu-back">
                        <span aria-hidden="true"></span>
                        Speed
                    </media-menu-back>
                    <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>

                <media-menu id="captions-menu" class="menu-panel">
                    <media-menu-back class="menu-back">
                        <span aria-hidden="true"></span>
                        Captions
                    </media-menu-back>
                    <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>
            </media-menu>
        </div>
    </media-container>
</video-player>

API Reference

media-menu

Props

PropTypeDefaultDetails

State

State is reflected as data attributes for CSS styling.

PropertyTypeDetails

Data attributes

AttributeTypeDetails

CSS custom properties

VariableDetails

media-menu-back

Button that navigates back to the parent menu view. Place at the top of a submenu Content.

Props

PropTypeDefaultDetails

media-menu-checkbox-item

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

Props

PropTypeDefaultDetails

Data attributes

AttributeTypeDetails

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-item-value

Displays the current value for a settings menu item from Menu.Item or Menu.Trigger context.

Data attributes

AttributeTypeDetails

media-menu-radio-group

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

Props

PropTypeDefaultDetails

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

media-menu-view

Root menu view inside the menu viewport.