# media-live-button

Accessible live indicator button that seeks to the live edge when pressed

## Import

```ts
import '@videojs/html/ui/live-button';
```

## Anatomy

```html
<media-live-button></media-live-button>
```

## Behavior

The live button indicates whether playback is at the live edge and acts as a “go to live” control. While playback is behind the edge, clicking seeks to the live edge — the end of the last `seekable` range. At the edge the button goes inactive (`aria-disabled`) and clicking is a no-op.

The button is also disabled whenever it has no live edge to seek to: the media is not live, the `seekable` ranges are still empty (before metadata loads, for example), or the last range ends at `Infinity`. It reports `disabled` in its state, sets `data-disabled`, and ignores activation until a seek target exists. Passing `disabled` forces the same state.

The button combines three player features: [live](https://videojs.org/docs/framework/html/reference/api/feature-live) for `liveEdgeStart` and `targetLiveWindow`, [time](https://videojs.org/docs/framework/html/reference/api/feature-time) for `currentTime` and the `seek()` action, and [buffer](https://videojs.org/docs/framework/html/reference/api/feature-buffer) for `seekable`. All three are included in the `liveVideoFeatures` and `liveAudioFeatures` presets, and the packaged live skins include the button.

Media counts as live while `targetLiveWindow` is not `NaN`. That value is how far behind the live edge viewers can seek: `0` means the stream is watchable only at the edge, and `Infinity` means the stream keeps its recording seekable so viewers can rewind and catch up, like a DVR. For on-demand media the button reports neither `live` nor `liveEdge` and stays inert.

The live edge is not a single instant: it begins 5 seconds before `liveEdgeStart`, the position the media reports live playback starting at, so playback that drifts slightly behind still counts as live. When the media does not report `liveEdgeStart`, the edge is the trailing 10 seconds of the seekable window instead.

Without children, the button renders the translated “Live” badge text.

See [Play live streams](https://videojs.org/docs/framework/html/guides/live-streams) for how live state flows through the player.

## Styling

| Attribute | Values | Description |
| --- | --- | --- |
| `data-live` | Present / absent | Present when the stream is live (or DVR) |
| `data-live-edge` | Present / absent | Present when playback is at the live edge |
| `data-disabled` | Present / absent | Present when the button is non-interactive (mirrors `aria-disabled`) |

Use `data-live-edge` to color the button red at the edge and gray behind it, and `data-disabled` to fade it while no live-edge target is available:

```css
media-live-button { color: gray; }
media-live-button[data-live-edge] { color: red; }
media-live-button[data-disabled] { opacity: 0.5; }
```

## Accessibility

Renders a `<button>` with an automatic `aria-label` that updates based on state: “Playing live” at the live edge, “Seek to live edge” while behind it. Override with the `label` prop, which accepts a text descriptor, literal string, or function returning either. A text descriptor contains an opaque translation key and English fallback, for example `{ key: 'live.playing', text: 'Playing live' }`. When the button is disabled — explicitly, or because no live-edge seek target is available yet — or playback is at the live edge, `aria-disabled="true"` is set and activation is ignored. Keyboard activation: Enter / Space.

## Examples

### Basic Usage

The demo plays a continuously running live stream. Seek backward to move behind the live edge, then press the Live button to jump back to it.

**index.html**

```html
<live-video-player class="html-live-button-basic">
  <media-container>
    <hlsjs-video src="https://stream.mux.com/v69RSHhFelSm4701snP22dYz2jICy4E4FUyk02rW4gxRM.m3u8" autoplay muted playsinline></hlsjs-video>
    <div class="html-live-button-basic__buttons">
      <media-seek-button seconds="-30" class="html-live-button-basic__seek">&#9194; 30s</media-seek-button>
      <media-live-button class="html-live-button-basic__live">
        <span class="html-live-button-basic__dot"></span>
        Live
      </media-live-button>
    </div>
  </media-container>
</live-video-player>
```

**index.css**

```css
.html-live-button-basic media-container {
  position: relative;
}

.html-live-button-basic media-container hlsjs-video {
  display: block;
  width: 100%;
}

.html-live-button-basic__buttons {
  position: absolute;
  bottom: 10px;
  left: 10px;
  display: flex;
  gap: 8px;
}

.html-live-button-basic__seek,
.html-live-button-basic__live {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding-block: 8px;
  padding-inline: 20px;
  color: black;
  white-space: nowrap;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.7);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 9999px;
  backdrop-filter: blur(10px);
}

.html-live-button-basic__live[data-live-edge] {
  cursor: default;
}

.html-live-button-basic__dot {
  width: 8px;
  height: 8px;
  background: gray;
  border-radius: 50%;
}

.html-live-button-basic__live[data-live-edge] .html-live-button-basic__dot {
  background: red;
}
```

**index.ts**

```ts
import '@videojs/html/live-video/player';
import '@videojs/html/ui/container';
import '@videojs/html/media/hlsjs-video';
import '@videojs/html/ui/live-button';
import '@videojs/html/ui/seek-button';
```

## API Reference

### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `disabled` | `boolean` | `false` | Whether the button is disabled. |
| `label` | `{ key: string; text: string } \| string \| ((state: LiveButtonState) => Text \| string)` | `''` | Custom label for the button. |

### State

State is reflected as data attributes for CSS styling.

| Property | Type | Description |
| --- | --- | --- |
| `label` | `{ key: string; text: string } \| string` | |
| `live` | `boolean` | Whether the stream is live (or DVR). |
| `liveEdge` | `boolean` | Whether playback is at the live edge. |
| `disabled` | `boolean` | Whether seeking to the live edge is temporarily unavailable. |

### Data attributes

| Attribute | Description |
| --- | --- |
| `data-live` | Present when the stream is live (or DVR). |
| `data-live-edge` | Present when playback is at the live edge. |
| `data-disabled` | Present when the button is non-interactive (mirrors `aria-disabled`). |

---

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