# Controls

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

## Import

```tsx
import { Controls } from '@videojs/react';
```

## Anatomy

Import the component and assemble its parts:

```tsx
<Controls.Root>
  <Controls.Backdrop />
  <Controls.Content>
    <Controls.Group />
  </Controls.Content>
</Controls.Root>
```

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

```tsx
<Controls.Root visibility="always">
  <Controls.Content>...</Controls.Content>
</Controls.Root>
```

## Styling

`Controls.Root` is a state and context provider. `Controls.Content` renders the interactive controls surface and receives its DOM props, ref, and controls state data attributes.

`Controls.Backdrop` is an optional presentational sibling of `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:

React renders `<div>` elements. Add a `className` to style them:

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

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

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

/* Fade transition */
.controls {
  transition: opacity 0.25s;
}

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

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

## Accessibility

`Controls.Root` renders no element. No ARIA role is applied to `Controls.Content` because it is a layout surface, not a landmark. `Controls.Backdrop` is always hidden from assistive technology. `Controls.Group` automatically receives `role="group"` when an `aria-label` or `aria-labelledby` attribute is provided; otherwise no role is assigned.

## Examples

### Basic Usage

**App.tsx**

```tsx
import { Container, Controls, createPlayer, PlayButton, Time } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';

const { Player } = createPlayer({ features: videoFeatures });

export default function BasicUsage() {
  return (
    <Player>
      <Container className="media-container">
        <Video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" autoPlay muted playsInline loop />

        <Controls.Root>
          <Controls.Backdrop className="controls-backdrop" />
          <Controls.Content className="media-controls">
            <Controls.Group className="controls-group" aria-label="Playback controls">
              <PlayButton
                className="button"
                render={(props, state) => <button {...props}>{state.paused ? 'Play' : 'Pause'}</button>}
              />

              <Time.Value type="current" className="time" />
            </Controls.Group>
          </Controls.Content>
        </Controls.Root>
      </Container>
    </Player>
  );
}
```

**App.css**

```css
.media-container {
  position: relative;
}

.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);
}
```

## API Reference

### Root

Manages controls state and provides it to the compound parts. Does not render an element.

#### Props

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

#### State

State is accessible via the `render`, `className`, and `style` props.

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

### Backdrop

Presentational layer behind player controls. Renders a `<div>` with the controls state data attributes so skins can style it without reaching across sibling components.

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string \| ((state: ControlsState) => string \| undefined)` | — | Class name or function returning class name from state. |
| `render` | `ReactElement \| ((props: HTMLProps, state: ControlsState) => ReactElement \| null)` | — | Render prop for custom element. |
| `style` | `CSSProperties \| ((state: ControlsState) => CSSProperties \| undefined)` | — | Style or function returning style from state. |

#### Data attributes

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

### Content

Renders the interactive controls surface.

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string \| ((state: ControlsState) => string \| undefined)` | — | Class name or function returning class name from state. |
| `render` | `ReactElement \| ((props: HTMLProps, state: ControlsState) => ReactElement \| null)` | — | Render prop for custom element. |
| `style` | `CSSProperties \| ((state: ControlsState) => CSSProperties \| undefined)` | — | Style or function returning style from state. |

#### Data attributes

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

### Group

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

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string \| ((state: ControlsState) => string \| undefined)` | — | Class name or function returning class name from state. |
| `render` | `ReactElement \| ((props: HTMLProps, state: ControlsState) => ReactElement \| null)` | — | Render prop for custom element. |
| `style` | `CSSProperties \| ((state: ControlsState) => CSSProperties \| undefined)` | — | Style or function returning style from state. |

#### Data attributes

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

---

React documentation: https://videojs.org/docs/framework/react/llms.txt
All documentation: https://videojs.org/llms.txt
