GuideConcepts
Accessibility
How Video.js approaches accessibility, and what you should consider if you're deeply customizing your player
Video.js is built with accessibility in mind so your player can reach the widest possible audience.
Here are some of the accessibility aspects we consider. You should consider them too if you’re performing deeper customization.
Standards we target
- WAI-ARIA 1.2 defines roles, states, and properties that assistive technologies use to understand controls.
- WCAG 2.2 provides success criteria for perceivable, operable, understandable, and robust web content.
- CVAA is a U.S. federal law requiring closed captioning and accessible controls for video content.
- ADA is a U.S. civil rights law that prohibits disability discrimination. Courts increasingly apply it to websites, including video players.
- European Accessibility Act applies across the EU from June 28, 2025, and covers services such as e-commerce and audiovisual media. EN 301 549 is the European standard for meeting it, and it incorporates WCAG.
These standards and laws are listed for context, not as legal advice. Check which ones apply to your product.
ARIA roles and labels
Components set ARIA roles and attributes automatically. For example, sliders render with role="slider" and value attributes, buttons update their aria-label based on player state, and decorative elements like time separators are hidden with aria-hidden="true".
Built-in labels are translated. See Internationalize the player to add or switch locales, and verify that labels are contextually correct for your content. Each component’s reference page documents the attributes it sets.
Tooltips are visual only. They are not linked to their trigger with aria-describedby, so a custom trigger needs its own accessible name. Built-in media buttons already have one.
Keyboard interaction
All interactive components support keyboard control. For example, buttons activate with Enter and Space, sliders respond to Arrow keys, Home, End, and Page Up/Page Down, and popovers close with Escape. Each component’s reference page lists its full keyboard support. We follow the patterns in the WAI-ARIA Authoring Practices Guide.
Player-wide shortcuts, such as Space to play or M to mute, and tap gestures come from hotkey and gesture components. The skins ship a full set, and custom UI declares its own. See Add keyboard shortcuts and gestures.
Focus management
Interactive elements are focusable when enabled and leave the tab order when disabled. Popover triggers link to their popups with aria-controls and aria-expanded. Menus and popovers close when focus moves away.
Dialogs move focus inside when they open, keep Tab within the dialog, and make the content behind them inert. Closing a dialog returns focus to its trigger, or to the element that had focus before it opened.
The player container is focusable and labeled: it takes role="group", tabindex="0", and an aria-label you can override. Keyboard users can tab to the player as a whole, and screen readers announce it as a named group.
Status announcements
The player announces confirmed state changes through a visually hidden live region (role="status"), so screen reader users hear when playback starts or pauses, captions turn on or off, fullscreen, picture-in-picture, playback rate, or volume change, and where a seek lands.
Announcements follow confirmed state from the player store rather than requested changes, so routine time updates never reach the live region. Rapid volume and seek changes are grouped into a single announcement, and the player stays quiet while one of its sliders has focus, since the slider already announces its own value. The skins include the status announcer. A custom layout needs to add it. State the store tracks is covered once it’s there; a custom control that tracks its own state needs its own live region.
Captions and subtitles
The text tracks feature supports captions and subtitles. Closed captions in the U.S. are governed by the FCC under the CVAA, which requires both captioning and viewer control over caption appearance (font, size, color, opacity, and edge style). Non-compliance carries legal risk.
Video.js leaves caption rendering to the browser, and you style cues with the ::cue pseudo-element. It doesn’t include a menu for caption appearance, so viewers rely on the caption preferences their browser and operating system provide. The WebKit Caption Display Settings explainer tracks how browsers are bringing these preferences into the web platform. See Show captions and subtitles to add tracks and a captions control.
Transcripts and audio description
Captions don’t cover every viewer. WCAG 2.2 also expects audio description or a text alternative for prerecorded video. A transcript can live inside the player but outside its container, so it reads player state without going fullscreen with the video. When a stream includes a described audio track, an audio track menu lets viewers choose it.
Color contrast
Skins respond to prefers-contrast: more by increasing background opacity on overlays and controls, and to prefers-reduced-transparency: reduce by raising baseline opacity so text stays readable over video.
Video is harder than most UI for contrast because the background is always changing. A control that passes checks over a dark frame can disappear over a bright title card. Automated tools won’t catch this, so test your controls against light, dark, and mixed video content. Custom components should test against WCAG 1.4.3 (contrast minimum) and 1.4.11 (non-text contrast).
Touch targets
Skin controls size from --media-scale-unit, which defaults to 16px, so they don’t grow with the user’s font size preference. Set it to 1rem to scale with the document root font size. See Customize skins.
Skin buttons set touch-action: manipulation to remove tap delay on touch devices, and sliders set touch-action: none so dragging doesn’t scroll the page. Skins use pointer: fine and pointer: coarse to adapt transitions for mouse and touch input.
If you build custom controls, target at least 24x24 CSS pixels per WCAG 2.5.8 (target size minimum). Larger is better on touch-first surfaces.
Color scheme
The audio skins switch their palette for light and dark mode with light-dark(), which follows the inherited color-scheme, not the user’s preference directly. Set color-scheme: light dark on the page or the player to follow the user’s setting. The video skins keep dark controls in both schemes; only their border follows it. See Customize skins.
Reduced motion
Skins respect prefers-reduced-motion by simplifying or removing animations and transitions. Follow the same pattern in custom components.
That covers the controls, not the video. Video.js doesn’t pause or hide autoplaying video for reduced-motion users; your page decides that.
Autoplay and moving video
WCAG 2.2.2 requires a way to pause, stop, or hide motion that starts automatically and lasts more than five seconds. A muted, looping hero video counts. Give it a pause control, or keep it short.
A purely decorative video should be hidden from assistive technology with aria-hidden="true" on its wrapper. If the video carries information, give it an accessible name and controls, and use a regular player. See Add a background video and Autoplay.
Test with assistive technology
Automated tools catch markup issues but miss real usability problems. Test your player with a screen reader (VoiceOver on macOS, NVDA or JAWS on Windows) and keyboard-only navigation. Verify that every control is reachable, operable, and announces its purpose and state. The WAI “Easy Checks” are a good starting point.
Further reading
The W3C WAI guides on making audio and video media accessible and accessible media players go deeper on everything covered here.