Skip to content
FrameworkStyle

StatusAnnouncer

Announce player state changes through a persistent status live region

Import

import { StatusAnnouncer } from '@videojs/react';

Anatomy

<StatusAnnouncer />

Behavior

StatusAnnouncer watches player-store snapshots, so it reports changes made by buttons, sliders, custom controls, or direct player actions. It does not depend on Hotkey or Gesture events.

The first snapshot establishes a baseline without announcing it. Later changes are handled in two groups:

Timing Changes
Immediate Playing or paused, captions on or off, fullscreen entered or exited, picture-in-picture entered or exited, and playback rate
Debounced by 200 milliseconds The final volume or mute value and the final time after a completed seek

Regular playback-time updates are ignored. A seek announcement is queued only after seeking changes from true to false at a different time. When several immediate states change in one snapshot, their translated labels are combined into one announcement.

The component always renders with role="status". Its [data-status-announcer-content] child is replaced for every announcement, including repeated text, so assistive technology receives a fresh live-region change. After closeDelay, which defaults to 800 milliseconds, the label is cleared while the status region remains rendered.

Announcements use the active Video.js locale and translations.

Use the labels prop to override individual translated labels for one announcer.

Styling

StatusAnnouncer is not visually hidden by the primitive. Apply a visually-hidden class while keeping it in the accessibility tree. Do not use display: none, visibility: hidden, or the hidden attribute.

Add a className to the component:

.status-announcer {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  white-space: nowrap;
  border: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
}

Accessibility

The status role provides implicit polite live-region behavior, so the component does not add an explicit aria-live attribute. Keep one announcer inside each player Container whose state changes should be reported.

Debounced volume and completed-seek announcements are suppressed while an element with role="slider" inside the same Container has focus. The focused slider provides its own value feedback, and suppressing the separate status message avoids duplicate announcements. A focused slider outside that Container does not suppress it.

Use SeekIndicator for temporary visual seek feedback. Keep that visual value out of a live region; StatusAnnouncer owns the accessible announcement.

Examples

Basic Usage

Use the playback button for an immediate announcement and the mute button for a debounced announcement. The sliders provide their own focused value feedback, so StatusAnnouncer suppresses its duplicate volume and completed-seek message.

These controls announce status changes to screen readers.

import {
  Container,
  createPlayer,
  MuteButton,
  PlayButton,
  StatusAnnouncer,
  TimeSlider,
  VolumeSlider,
} from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';

import './BasicUsage.css';

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

export default function BasicUsage() {
  return (
    <Player>
      <Container className="react-status-announcer-basic">
        <Video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" muted playsInline />
        <p className="react-status-announcer-basic__instructions">
          These controls announce status changes to screen readers.
        </p>
        <div className="react-status-announcer-basic__controls">
          <PlayButton
            className="react-status-announcer-basic__button"
            render={(props, state) => (
              <button {...props}>{state.ended ? 'Replay' : state.paused ? 'Play' : 'Pause'}</button>
            )}
          />
          <MuteButton
            className="react-status-announcer-basic__button"
            render={(props, state) => <button {...props}>{state.muted ? 'Unmute' : 'Mute'}</button>}
          />
          <VolumeSlider.Root className="react-status-announcer-basic__volume-slider">
            <VolumeSlider.Track className="react-status-announcer-basic__track">
              <VolumeSlider.Fill className="react-status-announcer-basic__fill" />
            </VolumeSlider.Track>
            <VolumeSlider.Thumb className="react-status-announcer-basic__thumb" />
          </VolumeSlider.Root>
        </div>
        <TimeSlider.Root className="react-status-announcer-basic__time-slider">
          <TimeSlider.Track className="react-status-announcer-basic__track">
            <TimeSlider.Buffer className="react-status-announcer-basic__buffer" />
            <TimeSlider.Fill className="react-status-announcer-basic__fill" />
          </TimeSlider.Track>
          <TimeSlider.Thumb className="react-status-announcer-basic__thumb" />
        </TimeSlider.Root>
        <StatusAnnouncer className="react-status-announcer-basic__announcer" />
      </Container>
    </Player>
  );
}

API Reference

Props

PropTypeDefaultDetails
closeDelaynumber
labelsPartial<StatusAnnouncerLabels>
shouldAnnouncefunction

State

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

PropertyTypeDetails
generationnumber
labelstring | null