# media-controls

Container component for composing and auto-hiding video player controls on user interaction

## Import

```ts
import '@videojs/html/ui/controls';
import '@videojs/html/ui/controls-backdrop';
import '@videojs/html/ui/controls-content';
import '@videojs/html/ui/controls-group';
```

## Anatomy

Import the component and assemble its parts:

```html
<media-controls>
  <media-controls-backdrop></media-controls-backdrop>
  <media-controls-content>
    <media-controls-group></media-controls-group>
  </media-controls-content>
</media-controls>
```

## Behavior

If the user is active, or if the video is paused, this component will show controls. Otherwise, it will hide them after a short delay.

User activity is tracked via pointer movement, keyboard input, and focus events on the player container. On touch devices, a quick tap toggles visibility. `mouseleave` immediately sets the user as inactive.

`visibility` selects between the two modes. The default, `auto`, follows the player’s controls visibility state as described above. `always` keeps the controls visible and reports the user as active regardless of playback or idle state, and works without the controls feature. The idle delay itself is not configurable.

```html
<media-controls visibility="always">
  <media-controls-content>...</media-controls-content>
</media-controls>
```

## Styling

`<media-controls>` is a state and context provider that also reflects the controls state data attributes. `<media-controls-content>` is the interactive controls surface and receives the same controls state data attributes.

`<media-controls-backdrop>` is an optional presentational sibling of `<media-controls-content>`. It receives the same controls state data attributes, allowing its styling and transitions to be authored independently from the controls surface.

By default, controls have the following styles:

```css
/* Click-through: clicks pass through controls to video beneath */
media-controls-content {
  pointer-events: none;
}

media-controls-group {
  pointer-events: auto;
}

media-controls-backdrop {
  position: absolute;
  inset: 0;
  transition: opacity 0.35s;
}

/* Fade transition */
media-controls-content {
  transition: opacity 0.25s;
}

media-controls-content:not([data-visible]) {
  opacity: 0;
}

media-controls-backdrop:not([data-visible]) {
  opacity: 0;
}
```

## Accessibility

No ARIA role is applied to `<media-controls>` or `<media-controls-content>` — they provide state and layout, not a landmark. `<media-controls-backdrop>` is always hidden from assistive technology. `<media-controls-group>` automatically receives `role="group"` when an `aria-label` or `aria-labelledby` attribute is provided; otherwise no role is assigned.

## Examples

### Basic Usage

**index.html**

```html
<video-player class="video-player">
  <media-container>
    <video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" autoplay muted playsinline loop></video>

    <media-controls>
      <media-controls-backdrop class="controls-backdrop"></media-controls-backdrop>
      <media-controls-content class="media-controls">
        <media-controls-group class="controls-group" aria-label="Playback controls">
          <media-play-button class="button media-play-button">
            <span class="paused">Play</span>
            <span class="playing">Pause</span>
          </media-play-button>
          <media-time class="time" type="current"></media-time>
        </media-controls-group>
      </media-controls-content>
    </media-controls>
  </media-container>
</video-player>
```

**index.css**

```css
.video-player media-container {
  position: relative;
  display: block;
}

.video-player media-container video {
  width: 100%;
}

.media-controls {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  padding: 12px;
  pointer-events: none;
  transition: opacity 0.25s;
}

.controls-backdrop {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.45), transparent 45%);
  opacity: 1;
  transition: opacity 0.35s;
}

.controls-backdrop:not([data-visible]),
.media-controls:not([data-visible]) {
  opacity: 0;
}

.controls-group {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  pointer-events: auto;
}

.time {
  display: inline-flex;
  gap: 4px;
  align-items: center;
  padding-block: 8px;
  padding-inline: 16px;
  font-size: 14px;
  color: black;
  background: rgba(255, 255, 255, 0.75);
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 9999px;
  backdrop-filter: blur(10px);
}

.button {
  padding-block: 8px;
  padding-inline: 16px;
  font-size: 14px;
  color: black;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.75);
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 9999px;
  backdrop-filter: blur(10px);
}

.button .paused,
.button .playing {
  display: none;
}

.media-play-button[data-paused] .paused {
  display: inline;
}

.media-play-button:not([data-paused]) .playing {
  display: inline;
}
```

**index.ts**

```ts
import '@videojs/html/video/player';
import '@videojs/html/ui/container';
import '@videojs/html/ui/controls';
import '@videojs/html/ui/controls-backdrop';
import '@videojs/html/ui/controls-content';
import '@videojs/html/ui/controls-group';
import '@videojs/html/ui/play-button';
import '@videojs/html/ui/time';
```

## API Reference

### media-controls

Tracks controls visibility, reflects it as data attributes, and shares it with its descendant controls parts. Hiding the controls closes any popup open inside it.

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `visibility` | `'auto' \| 'always'` | `'auto'` | Whether controls follow player visibility state or remain visible. |

#### State

State is reflected as data attributes for CSS styling.

| Property | Type | Description |
| --- | --- | --- |
| `visible` | `boolean` | Whether the controls are visible. |
| `userActive` | `boolean` | Whether the user has recently interacted with the player. |

#### Data attributes

| Attribute | Description |
| --- | --- |
| `data-visible` | Present when controls are visible. |
| `data-user-active` | Present when the user has recently interacted. |

### media-controls-backdrop

Presentational backdrop that reflects its owning controls surface's state.

#### Data attributes

| Attribute | Description |
| --- | --- |
| `data-visible` | Present when controls are visible. |
| `data-user-active` | Present when the user has recently interacted. |

### media-controls-content

Interactive surface that reflects its owning controls state.

#### Data attributes

| Attribute | Description |
| --- | --- |
| `data-visible` | Present when controls are visible. |
| `data-user-active` | Present when the user has recently interacted. |

### media-controls-group

Layout group for related controls; sets `role="group"` when labeled.

#### Data attributes

| Attribute | Description |
| --- | --- |
| `data-visible` | Present when controls are visible. |
| `data-user-active` | Present when the user has recently interacted. |

---

HTML documentation: https://videojs.org/docs/framework/html/llms.txt
All documentation: https://videojs.org/llms.txt
