# media-mute-button

Accessible mute/unmute button with keyboard support and volume state reflection

## Import

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

## Anatomy

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

## Behavior

Toggles mute on and off, and exposes a derived `volumeLevel` based on the current volume and mute state.

Mute availability is separate from volume-level availability because some media can accept a mute command without supporting volume changes.

When mute is unavailable or unsupported, the element receives the native `hidden` attribute. Toggling is also ignored while mute is unavailable.

## Styling

| Attribute | Values | Description |
| --- | --- | --- |
| `data-muted` | Present / absent | Present when the media is muted |
| `data-volume-level` | `"off"` \| `"low"` \| `"medium"` \| `"high"` | Current volume level |
| `data-availability` | `"available"` \| `"unavailable"` \| `"unsupported"` | Whether the media can be muted |
| `data-hidden` | Present / absent | Present on the HTML element while mute is unavailable or unsupported |

Style the button based on muted state:

```css
media-mute-button[data-muted] .icon-muted { display: inline; }
media-mute-button:not([data-muted]) .icon-unmuted { display: inline; }
```

Use `data-volume-level` for multi-level icon switching:

```css
media-mute-button[data-volume-level="off"] .icon-off { display: inline; }
media-mute-button[data-volume-level="low"] .icon-low { display: inline; }
media-mute-button[data-volume-level="medium"] .icon-medium { display: inline; }
media-mute-button[data-volume-level="high"] .icon-high { display: inline; }
```

Unavailable and unsupported buttons are hidden automatically. No availability selector or extra hiding CSS is required.

## Accessibility

Renders a `<button>` with an automatic `aria-label`: “Unmute” when muted, “Mute” when unmuted. Override with the `label` prop. Keyboard activation: Enter / Space.

## 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-mute-button class="media-mute-button">
      <span class="muted">Unmute</span>
      <span class="unmuted">Mute</span>
    </media-mute-button>
  </media-container>
</video-player>
```

**index.css**

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

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

.media-mute-button {
  position: absolute;
  bottom: 10px;
  left: 10px;
  padding-block: 8px;
  padding-inline: 20px;
  color: black;
  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);
}

.media-mute-button .muted {
  display: none;
}
.media-mute-button .unmuted {
  display: none;
}
.media-mute-button[data-muted] .muted {
  display: inline;
}
.media-mute-button:not([data-muted]) .unmuted {
  display: inline;
}
```

**index.ts**

```ts
import '@videojs/html/video/player';
import '@videojs/html/ui/container';
import '@videojs/html/ui/mute-button';
```

### Volume Levels

**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-mute-button class="media-mute-button">
      <span class="level-off">Off</span>
      <span class="level-low">Low</span>
      <span class="level-medium">Medium</span>
      <span class="level-high">High</span>
    </media-mute-button>
  </media-container>
</video-player>
```

**index.css**

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

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

.media-mute-button {
  position: absolute;
  bottom: 10px;
  left: 10px;
  padding-block: 8px;
  padding-inline: 20px;
  color: black;
  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);
}

.media-mute-button .level-off,
.media-mute-button .level-low,
.media-mute-button .level-medium,
.media-mute-button .level-high {
  display: none;
}

.media-mute-button[data-volume-level="off"] .level-off {
  display: inline;
}
.media-mute-button[data-volume-level="low"] .level-low {
  display: inline;
}
.media-mute-button[data-volume-level="medium"] .level-medium {
  display: inline;
}
.media-mute-button[data-volume-level="high"] .level-high {
  display: inline;
}
```

**index.ts**

```ts
import '@videojs/html/video/player';
import '@videojs/html/ui/container';
import '@videojs/html/ui/mute-button';
```

## API Reference

### Props

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

### State

State is reflected as data attributes for CSS styling.

| Property | Type | Description |
| --- | --- | --- |
| `muted` | `boolean` | Whether audio is muted. |
| `label` | `{ key: string; text: string } \| string` | |
| `volumeLevel` | `'off' \| 'low' \| 'medium' \| 'high'` | Derived volume level:<br>- `off`: muted or volume is 0<br>- `low`: volume < 0.5<br>- `medium`: volume < 0.75<br>- `high`: volume >= 0.75 |
| `availability` | `'available' \| 'unavailable' \| 'unsupported'` | Whether the media can be muted at all. |
| `hidden` | `boolean` | Whether the button is hidden because the media has no mute to toggle. |

### Data attributes

| Attribute | Type | Description |
| --- | --- | --- |
| `data-muted` | — | Present when the media is muted. |
| `data-volume-level` | `'off' \| 'low' \| 'medium' \| 'high'` | Indicates the volume level. |
| `data-availability` | `'available' \| 'unavailable' \| 'unsupported'` | Indicates mute availability (`available`, `unavailable`, `unsupported`). |
| `data-hidden` | — | Present when the button is hidden because the media has no mute to toggle. |

---

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