Back to Remotion

Accessibility Review — @remotion/player (video-player component)

packages/compliance/a11y/player/RGAA-2026-04-14.md

4.0.45716.0 KB
Original Source

Accessibility Review — @remotion/player (video-player component)

Review date: 14.04.2026 Standard referenced: RGAA 4.1.2 (maps to WCAG 2.1 AA / EN 301 549) Component under review: <Player> from @remotion/player Instance tested: Demo Player on https://www.remotion.dev/player ("Your favorite color is …" composition) Test environment: Chrome + live DOM inspection Method: DOM / accessibility-tree inspection via DevTools + scripted queries; manual focus/label checks. Scope note: This review covers the Player component (container semantics, play/pause, mute, seek, volume, fullscreen, time display). It does not cover page-level issues on /player (marketing-page headings, surrounding demo "color-selection controls", duplicate link text, etc.) — those belong to the page, not the component.


What is conformant

AreaFinding
Play / Pause buttonNative <button type="button">; accessible name toggles between aria-label="Play video" and aria-label="Pause video" to reflect state. Focusable via Tab.
Mute buttonNative <button type="button"> with aria-label="Mute sound" and matching title. Focusable via Tab.
Fullscreen buttonNative <button type="button"> with aria-label="Enter Fullscreen" and matching title. Focusable via Tab.
Time display textRendered as real text in the DOM ("0:10 / 0:11") — readable by screen readers when reached.
Composition render surfaceCompositions render to real DOM (no <canvas>/<video>), so text inside a composition (e.g. "Your favorite color is …") is announceable by screen readers.
Decorative <audio> tags5 preload <audio> elements live in the DOM but are display: none, so they do not appear in the visual tab order in practice.

Findings

Theme 9 — Container semantics

#CriterionStatusObservation
9.2Are landmarks / regions present and relevant?NC.__remotion-player wrapper has no role, no aria-label, no aria-roledescription. Screen-reader users meet an unlabelled group of buttons with no indication that this is a video player. Add role="region" + aria-label="Video player" (or aria-roledescription="video player").

Theme 4 — Multimedia

#CriterionStatusObservation
4.2Prerecorded synchronized media has captions?NC<Player> does not render a <video>/<track> hierarchy and exposes no captions/subtitles mechanism in the component API. For compositions with spoken dialogue, captions are impossible without consumer code. Add a first-class captions / <track>-equivalent surface.
4.3Audio description or text alternativeNCNo mechanism in the component for audio descriptions or a synchronized text alternative.
4.12Are all media-player controls correctly labeled?C*Play/Pause, Mute, Fullscreen each have aria-label and title. But the Mute button label does not toggle to "Unmute sound" after activation, and carries no aria-pressed — see 7.1.
4.13Pause / stop / hide for auto-playing mediaNCA <Player autoPlay loop> instance has no built-in pause/stop/hide for users with prefers-reduced-motion. Documentation / API gap.

Theme 7 — Scripts

#CriterionStatusObservation
7.1Is each script compatible with assistive technologies?NCSeek bar is built from plain nested <div> elements (five divs in a 448 px stack, heights 5/8/13 px). No role="slider", no aria-valuemin / aria-valuemax / aria-valuenow, no aria-label. Screen-reader users cannot perceive progress nor a seek affordance. Volume slider (the 65 px <div> next to the mute button) is likewise an unannotated <div>. Mute button has no aria-pressed and the label does not toggle ("Mute sound" stays "Mute sound"); muted state is not programmatically exposed.
7.2Is each script controllable by keyboard?NCSeek bar is not keyboard-operable (not in tab order; no key handlers). There is no way to scrub forward/back with the keyboard. Volume slider is not keyboard-operable. The component provides no media keyboard shortcuts on the player root: Space/K, ←/→, ↑/↓, M, F all do nothing.
7.5Are status messages correctly rendered by assistive technologies?NCTime display ("0:10 / 0:11") updates silently each frame — no role="timer", no aria-live. Mute state changes are not announced. Add aria-pressed on mute (or a polite live region) and expose current time via aria-valuetext on the seek slider.

Theme 10 — Focus visibility

#CriterionStatusObservation
10.7Focus visibleCKeyboard-focused Play/Pause, Mute, and Fullscreen do show a visible focus ring (screenshot confirmed: blue outline on the Pause button). The author CSS sets outline-style: none on the :focus state, but Chrome's default :focus-visible UA style restores a ring for keyboard focus. Recommendation (not a failure): define an explicit :focus-visible style on the component (outline: 2px solid or box-shadow) so the indicator doesn't depend on UA defaults across browsers/themes.

Theme 12 — Tab order

#CriterionStatusObservation
12.8Logical tab orderCThe three visible controls follow visual order (Play → Mute → Fullscreen). The seek and volume sliders are unreachable from the keyboard (see 7.2) — a separate gap rather than a tab-order inconsistency.

Recommendations (component-level fixes)

PriorityFix
CriticalMake the seek bar keyboard-operable and expose it as a slider: either an <input type="range"> styled to match, or a div with role="slider" + aria-valuemin/max/now/text + tabindex="0" + key handlers (←/→ seek, Home/End jump).
CriticalMake the volume slider keyboard-operable with the same pattern and a label (aria-label="Volume").
LowDefine an explicit :focus-visible style on the control buttons so the focus ring isn't dependent on Chrome's UA default (swap outline-style: none on :focus for a defined outline or box-shadow on :focus-visible).
HighAdd aria-pressed to the Mute button and let its label reflect state ("Mute sound" / "Unmute sound") so screen readers announce state changes.
HighAdd first-class captions / <track>-equivalent surface to the component API so authors can provide WebVTT captions for compositions with dialogue.
HighProvide a transcript slot (or documented pattern) for compositions with speech.
MediumGive the player wrapper a landmark role and accessible name: role="region" + aria-label="Video player" (or aria-roledescription="video player").
MediumRespect prefers-reduced-motion for <Player autoPlay loop>: by default, do not autoplay/loop; document the override.
MediumAdd standard media keyboard shortcuts when the player region is focused: Space / K (play-pause), ←/→ (5 s seek), Shift+←/→ (fine seek), ↑/↓ (volume), M (mute toggle), F (fullscreen).
LowExpose time via aria-valuetext on the seek slider, or make the time display a polite live region with explicit announcements on pause/seek.

Declaration

The Remotion Player component correctly labels its three visible control buttons but is not a fully accessible media player: seek and volume sliders carry no ARIA slider semantics and are not keyboard-operable, the focus indicator on all controls is suppressed, mute state is not programmatically exposed, the player region has no landmark/label, and the component offers no captions, transcript, audio-description, or reduced-motion surface.

Gaps are concentrated in 2.1.1 (Keyboard), 4.1.2 (Name, Role, Value), 2.4.7 (Focus Visible), 1.2.2 / 1.2.3 (Captions / descriptions), and 1.3.1 (Info and Relationships) at the media-player region level.

Reference: https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/