# Automatically show and hide controls

Show controls while the user is active, hide them during playback, and toggle them by touch.

> **Note**
>
> Using a pre-built [skin](https://videojs.org/docs/framework/html/guides/skins)? It already includes the controls shown here. You may still need the media or player setup in this guide. The component examples are for building your own player UI from individual [components](https://videojs.org/docs/framework/html/guides/ui-components).

## Recommended approach

Wrap your control UI in [`<media-controls>`](https://videojs.org/docs/framework/html/reference/components/controls). It reads controls visibility from player state, so the whole control bar shows and hides as one unit.

**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';
```

## How it works

The [controls feature](https://videojs.org/docs/framework/html/reference/api/feature-controls) tracks activity on the player container:

- `userActive` is `true` while the user interacts — pointer movement, keyboard input, or focus — and flips to `false` after two seconds of inactivity.
- `controlsVisible` derives from activity and playback: controls stay visible while paused or while a remote playback session is connecting or connected, regardless of idle state.
- `toggleControls()` flips visibility, for wiring to custom triggers.

On touch devices, a tap on the media toggles the controls instead of relying on pointer movement, and mid-gesture movement doesn’t flicker visibility.

The feature needs the player container for activity tracking; without a container it stays inactive and warns in development.

## Availability and constraints

- Activity tracking listens on the player container, so controls placed outside it don’t keep the player “active” — put control UI inside the container.
- Keyboard focus counts as activity: controls stay reachable for keyboard and assistive-technology users while focus is inside them.
- Touch and mouse behave differently by design — mouse movement shows controls, touch requires a tap. Test both.
- The idle delay is fixed at two seconds.

## Common variations

### Always-visible controls

Set `visibility="always"` on [`<media-controls>`](https://videojs.org/docs/framework/html/reference/components/controls). The controls stay visible and report the user as active while the feature keeps tracking activity for anything else that needs it. The packaged audio skins use this mode.

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

### Custom show/hide styling

Controls hide by state, so you own the transition. Style the visible and hidden states in CSS — fade, slide, or scale — driven by the component’s rendered state rather than re-implementing the idle timer.

## Troubleshooting

### Controls never hide

The media is paused (controls intentionally stay visible), a remote playback session is active, an open menu or popover holds a controls lock, or something keeps triggering activity — for example a focused element inside the container receiving repeated events.

### Controls don’t come back on touch

A tap on the media toggles controls. If a custom overlay covers the media and swallows pointer events, taps never reach the container; let pointer events pass through decorative overlays.

### Controls flicker on Android while tapping

Use the skin gestures (`tap` with the `toggleControls` action) rather than adding your own tap handlers next to the built-in behavior; competing handlers invert each other. See [Add keyboard shortcuts and gestures](https://videojs.org/docs/framework/html/guides/keyboard-shortcuts).

## Related pages

### Components

- [media-controls](https://videojs.org/docs/framework/html/reference/components/controls): Container component for composing and auto-hiding video player controls on user interaction
- [media-container](https://videojs.org/docs/framework/html/reference/components/player-container): The player's visual and interaction surface for layout, fullscreen, focus, and user activity.

### API

- [Controls](https://videojs.org/docs/framework/html/reference/api/feature-controls): User activity and controls visibility state for the player store

### Guides

- [Add keyboard shortcuts and gestures](https://videojs.org/docs/framework/html/guides/keyboard-shortcuts): Add keyboard shortcuts, tap and double-tap gestures, and on-screen feedback for input actions.
- [Accessibility](https://videojs.org/docs/framework/html/guides/accessibility): How Video.js approaches accessibility, and what you should consider if you're deeply customizing your player

---

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