Search topics
Videos a keyboard can't play, or with no captions button
A product video's play button, drawn over the poster, takes only clicks and says nothing, or the player the site built has no button for the captions and the described version the video has.
<div class="big-play"></div> The problem
Keyboard and screen reader users can't start the video, so they never reach its captions, and in a player with no captions button, nobody can turn on the captions or the described version.
The coffee roaster's brewing video has checked captions and a described version. To match the site, the shop had its own player built: the poster with a round play button in the middle, and, once the video starts, a dark bar with the progress line, play, the time, volume, and CC and AD. The round play button is a div with a click handler, and the bar stays hidden until it is clicked. With a mouse, it all works. With a keyboard, Tab goes from the heading straight to "Shop the house blend" under the video: the play button is never a stop, so the video never starts and the bar, with its captions button, never appears. A screen reader moving down the page finds nothing to press where the video is, because the play button has no role and no name.
Another common player gets the buttons right and leaves something out. Its play button and its bar are real buttons and sliders, each with a name, but the bar was drawn with play, the progress line, mute and volume only. Now the keyboard starts the video and a screen reader hears what each control is, but the captions file on the video never shows, and the described version on the server never plays, because nothing in the player turns either on.
Success criterion 2.1.1 Keyboard asks that everything a mouse can do, a keyboard can do. Starting the video is done only by clicking a div, which takes no focus and listens for nothing but a click, so the keyboard can't do it, and everything the bar does waits behind that first click. Success criterion 4.1.2 Name, Role, Value asks that each control expose its name and its role. The div exposes neither: its triangle is drawn by CSS, so there is no text to read and nothing says it can be pressed. W3C's failure F59 describes this, script that makes a div a control without giving it a role, and failure F15 the wider case of custom controls that don't use the accessibility API.
The second player passes both criteria and fails two requirements EN 301 549 sets for anything that plays video with sound. Clause 7.1.1 asks for a mode of operation that displays the captions the video has, and clause 7.2.1 for a mechanism to select and play the audio description it has. The captions track is on the video and the described version is on the server, but this player can show neither. The browser's own controls would have shown the captions; the custom bar replaced them and left captions out. Neither clause is in WCAG, but Table A.1 of EN 301 549 lists both among the requirements for web pages, and meeting the clauses in that table gives a presumption of conformity with the EU's web accessibility directive.
A video is often where a shop explains its product. In the first player, the recipe plays only for people who use a mouse. In the second, the captions and the described version, made for the people who need them most, are there and can't be turned on.
5 of 10 groups affected
- No vision (affected)
- Low vision (affected)
- Colour vision (not affected)
- No hearing (affected)
- Hard of hearing (affected)
- No speech (not affected)
- Motor (affected)
- Reach, strength (not affected)
- Cognitive (not affected)
- Seizures (not affected)
- Limited manipulation Tab goes from the heading to the link under the video, so keyboard, switch and voice control users can't start it, and the bar, with its captions button, never appears. Voice control finds nothing called Play to click.
- Without vision A screen reader finds nothing to press where the video is: the play button has no role and no name. In the player with no AD button, the described version made for blind viewers never plays.
- Without hearing The video carries checked captions, and a deaf viewer has no way to turn them on: in one player only after starting it with a mouse, in the other not at all.
- Limited hearing People who catch some of the narration turn captions on to fill the gaps, and here they can't, or only with a mouse.
- Limited vision People who zoom in or use a magnifier, and lose track of a small pointer, move through the page with the keyboard instead, and Tab never stops on the play button.
A div with a click handler has no role, so axe has nothing to check. EaseWeb's audit reports a div or span in a video's player that sets a pointer cursor of its own, has a class word such as play, pause, mute or captions, and is never reached by pressing Tab; a play layer that covers a quarter of the picture or more counts only when nothing else in the player can be reached. A control named some other way, or with no pointer cursor, is not found by this check, though one with an inline onclick or a button role is still reported as an unreachable control. When the play button is a real button with only an icon, axe reports the missing name as button-name. No check can tell whether focus moves on when the play button goes. For a player with its own controls, the audit's review note on each video says whether it found a captions button near it and, when it found none, asks a person to confirm the player can show the captions and play the described version. AI fixes can add role="button" and tabindex="0" to the div and stop there, which gives a Tab stop that Enter and Space do nothing on; or make it a real button and hide it while it has focus, so focus is left on nothing.
Try it
Using only the keyboard, play the brewing video and turn on its captions.
In Broken 1, the play button on the picture takes only clicks: Tab goes from the heading straight to the link under the video, and the panel under the player shows that a screen reader finds nothing in it. Click it with the mouse to see the rest of the player. In Broken 2, the play button works from the keyboard, but nothing in the bar turns on the captions or the described version. Apply a fix and try again.
Broken Can you fix this?
Moving through the player, a screen reader says:
(nothing: no button, no text)
The fix
Make the play button over the poster a named button that hands focus on to the bar, show the bar from the start, and give it captions and audio description buttons whenever the video has them, or use the browser's own controls.
- 1Every control, the play button over the poster included, is a native button or slider, or the browser's own.
- 2Each says what it does, and when one goes away, focus moves on to what replaces it.
- 3Whenever the video has captions or a described version, the player can turn them on.
A play button over the poster that takes only clicks
<div class="player"> <video src="brew-guide.webm" poster="brew-guide.jpg"> <track kind="captions" srclang="en" label="English" src="brew-guide.en.vtt"> </video> <div class="big-play"></div> <div class="player-bar" hidden> <input type="range" class="seek" aria-label="Position" min="0" step="1" value="0"> <button type="button" class="play" aria-label="Play"></button> <span class="time">0:00</span> <button type="button" class="mute" aria-label="Mute" aria-pressed="false"></button> <input type="range" class="volume" aria-label="Volume" min="0" max="1" step="0.1" value="1"> <button type="button" class="captions" aria-pressed="false">CC<span class="visually-hidden"> captions</span></button> <button type="button" class="described" aria-pressed="false">AD<span class="visually-hidden"> audio description</span></button> </div></div>A play button and a bar, with captions and audio description
<div class="player"> <video src="brew-guide.webm" poster="brew-guide.jpg"> <track kind="captions" srclang="en" label="English" src="brew-guide.en.vtt"> </video> <button type="button" class="big-play" aria-label="Play video"></button> <div class="player-bar"> <input type="range" class="seek" aria-label="Position" min="0" step="1" value="0"> <button type="button" class="play" aria-label="Play"></button> <span class="time">0:00</span> <button type="button" class="mute" aria-label="Mute" aria-pressed="false"></button> <input type="range" class="volume" aria-label="Volume" min="0" max="1" step="0.1" value="1"> <button type="button" class="captions" aria-pressed="false">CC<span class="visually-hidden"> captions</span></button> <button type="button" class="described" aria-pressed="false">AD<span class="visually-hidden"> audio description</span></button> </div></div>Why this fix: Use native HTML first
The play button is a div that responds only to click: no Tab stop, no role and no name, and its triangle is drawn by CSS, so there is nothing to read. Everything else in the player waits behind it. The bar is hidden until that first click, so a keyboard user never reaches its buttons either, however well they are built.
The play button over the poster is a native button, so Tab reaches it, Enter and Space press it, and a screen reader calls it "Play video, button", with no key handling to write. Pressing it hides it, so the script moves focus to the bar's play button, which says Pause once the video is playing; without that line, the browser would move focus back to the page itself, away from the player. The bar is there from the start, not only after the first press, so captions and audio description take one press, as play does, and can be on before the video starts. In the bar, the CC button shows the captions track the video already carries, and the AD button swaps in the described version at the same place, as in videos without audio description of what they show. Both say whether they are on with aria-pressed, and their names start with the letters they show.
Both pass 2.1.1 and 4.1.2 and let viewers turn on the captions and the described version. Building the play button and the bar from native buttons and range inputs keeps the shop's own look, puts CC and AD in the bar beside volume, and costs the script above, which the browser would otherwise supply. The browser's own controls take one attribute, but they can't be made to match the site, the audio description button sits outside them, and in Chrome the captions are a menu down from volume. Use the native buttons and sliders: they are the only one of the two that puts captions beside volume in every browser, as Section 508 503.4.1 and EN 301 549 7.3 ask. The browser's controls are a quick repair for a page where neither of those applies, until the player is rebuilt.
Name the play button for what it plays. "Play video" is enough when the page has one video; with several, each needs its own name, such as "Play: How to brew our house blend", from aria-label or from aria-labelledby pointing at the video's heading. The whole poster can respond to a click as well, as a convenience for the mouse, as long as the round button is there for the keyboard.
Keep the bar there before the video starts. A player that shows only the play button until the first press puts captions and audio description a level below play, so a viewer who needs captions has to start the video without them. EN 301 549 7.3 asks for them at the same level as the main controls on a page that mainly shows the video, as video players with no captions button in the control bar explains. If the design keeps the poster clear, put a captions button beside the play button on the poster, as one of the design ideas below does.
The bar's own controls follow the same rules as any other.
- The play button changes its name, Play or Pause, and nothing else. The ARIA Authoring Practices say a toggle button's label must not change when its state does, so a button either changes its name or keeps one name and reports
aria-pressed, never both. - Mute, CC and AD keep their names and report whether they are on with
aria-pressed. - The position slider gives the time in words through
aria-valuetext, such as "12 seconds of 24 seconds", so a screen reader doesn't read a bare number. A progress line drawn as a div needsrole="slider",tabindex="0", every arrow, Home and End key and all of itsaria-valueattributes in script, whichinput type="range"already does.
Keep the bar where the keyboard can see it. A bar that fades out a few seconds into the video comes back when focus moves into the player and stays while focus is in it, or a keyboard user Tabs onto controls they can't see. Each control, and the play button over the poster, shows a focus ring that stands out against the dark bar and against both the light and dark parts of the picture, as missing focus indicators on custom buttons covers.
Keyboard shortcuts such as Space or K to play, M to mute and C for captions are a welcome extra, not a replacement for the buttons. A single-letter shortcut that only works while the player has focus is allowed under 2.1.4; one that works anywhere on the page is one of the single-key shortcuts that can't be turned off.
Show only what the video has: a CC button when there is a captions track, and an AD button when there is a described version. Where those buttons sit and how many presses they take is in video players with no captions button in the control bar, and the captions' size and colours in captions stuck in one style, or lost when video is converted. A poster that loads a YouTube or Vimeo player when it is clicked, to keep the page fast, needs the same native play button; once the embed has loaded, its controls are the platform's, so check what it really renders with the steps below.
Fixing this with an AI coding assistant? Get this guide as Markdown, or get every guide in your coding agent.
Design ideas
A video can start from a poster that looks good and still works from the keyboard.
Five ways to draw how the brewing video starts. Tab to each and try to turn on the captions before you start it: three start from the keyboard and offer the captions, two don’t.
In the design file
- Draw the play button as a button, with its focus ring, and write beside it the name a screen reader will say, such as Play video.
- After the first press: draw the player with its bar, and mark the Pause button as where focus lands.
- Put the captions button, and the audio description button when there is a described version, where they show before the video starts, in a bar that is always there or beside Play on the poster.
- Draw the focus ring over the lightest and the darkest part of the poster, so it shows on both.
Passes
A play pill with the running time A pill in the poster’s corner says Watch and 0:24, white on #1b1b1b at 17.22:1, so it reads as a button and tells people how long the video is before they start. The bar under the picture is there from the start, so CC and AD are one press too. Tab stops on the pill, Enter starts the video, and the yellow ring shows past a dark gap on any part of the picture.- The whole poster as one button The poster, its title on a solid #1b1b1b band (17.22:1) and a small play mark make one button, named by the title, so a screen reader says what will play, and the bar under it is there from the start, so the captions are one press before the video plays. The ring goes round the whole card, which suits a grid of videos where each card plays its own.
CC and AD beside Play, on the poster Play, CC and AD sit together on the poster, so a clean poster with no bar still lets people turn on captions or audio description before they start, in one press each. The chips are white on #1b1b1b at 17.22:1, filled yellow when on, and the bar that appears once the video plays keeps the same choice.
Fails
PlayA play bubble that follows the pointerWCAG 2.1.1 A round Play bubble trails the mouse across the poster, and a click anywhere starts the video. There is no button under it, so Tab goes straight past the video, and on a touch screen there is no pointer for the bubble to follow.Rule of thumb: Use native HTML first
A muted loop with only a sound chipEN 301 549 7.1.1 The video loops with the sound off and one round chip to turn it on. The narration has captions, but nothing shows them, so turning the sound on is the only way to the recipe. Drawn still here; on a site it loops.
Verify the fix
4 checks, no mouse.
Test with: Keyboard, Screen reader
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after updating the player or its library.
Limits
This is a starting pattern, not a guarantee for every application. It covers how the player's controls are reached, what they say, and whether the captions and the described version can be turned on. What is in them is covered elsewhere: videos without captions, or with unchecked automatic ones for the captions, and videos without audio description of what they show for the description. A video that starts with its sound on needs a way to stop it first, in autoplaying sound with no way to stop it, and one that moves by itself beside other content needs a pause, in tickers, carousels and animations that can't be paused. Test the complete journey in the browsers and with the assistive technology your viewers use.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
References
- WCAG Understanding: keyboard (opens in a new tab)
- WCAG Understanding: name-role-value (opens in a new tab)
- WCAG Technique F59 (opens in a new tab)
- WCAG Technique F15 (opens in a new tab)
- etsi.org: en_301549v030201p.pdf (opens in a new tab)
- w3.org: player (opens in a new tab)
- ARIA APG: button (opens in a new tab)
- developer.mozilla.org: hidden (opens in a new tab)
- developer.mozilla.org: mode (opens in a new tab)
- chromium.googlesource.com: media_controls_impl.cc (opens in a new tab)
Keep this fixed as your code changes.
Turn guides like this into rules your coding agents and CI follow, then have the result retested with a keyboard and screen readers.