Navigation Menu

Skip to content


fix(a11y): current time and duration display accessibility with Voice…
Browse files Browse the repository at this point in the history
…Over (#5653)

The desired behavior when navigating any of the TimeDisplay components (current time, duration, remaining time) with the arrow keys with a screen reader is for the singular parent div to receive focus, and the reader should announce the text of each child span in order, ex. "Current Time 0:17" and "Duration 1:10". This is how it works when using JAWS. However, with VoiceOver each individual child span element receives focus and the contents are announced separately.

According to the ARIA spec (see [here]( and [example 8](, there is no implicit role for span elements, so the contents should be exposed but the elements themselves should be invisible to screen readers. In other words, <span> Sample Content </span> should be the same as <span role="presentation"> Sample Content </span>. But this is not the case with VoiceOver. As far as I can tell, the desired behavior is only achieved with VoiceOver if each child span is explicitly made presentational, either by assigning each of them the presentation role, or by assigning the parent div a role that makes its children presentational (although there doesn't seem to be a role whose semantics fit the purpose of that div)

The first ARIA doc link above shows the Mac OS X Accessibility Protocol mapping for span elements is to the group role. I don't know enough about accessibility API mapping to confidently draw conclusions about root cause, but this seems like a possible explanation.

* add role="presentation" to each of the two span elements inside TimeDisplay divs
* add aria-hidden="true to the TimeDivider (tangential improvement)
* fix inaccurate description of the TimeDisplay component (tangential improvement)
  • Loading branch information
alex-barstow authored and gkatsev committed Dec 11, 2018
1 parent b9d8744 commit 8932611
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 3 deletions.
11 changes: 8 additions & 3 deletions src/js/control-bar/time-controls/time-display.js
Expand Up @@ -8,7 +8,7 @@ import {bind, throttle} from '../../utils/fn.js';
import formatTime from '../../utils/format-time.js';

* Displays the time left in the video
* Displays time information about the video
* @extends Component
Expand Down Expand Up @@ -39,14 +39,19 @@ class TimeDisplay extends Component {
const className = this.buildCSSClass();
const el = super.createEl('div', {
className: `${className} vjs-time-control vjs-control`,
innerHTML: `<span class="vjs-control-text">${this.localize(this.labelText_)}\u00a0</span>`
innerHTML: `<span class="vjs-control-text" role="presentation">${this.localize(this.labelText_)}\u00a0</span>`

this.contentEl_ = Dom.createEl('span', {
className: `${className}-display`
}, {
// tell screen readers not to automatically read the time as it changes
'aria-live': 'off'
'aria-live': 'off',
// span elements have no implicit role, but some screen readers (notably VoiceOver)
// treat them as a break between items in the DOM when using arrow keys
// (or left-to-right swipes on iOS) to read contents of a page. Using
// role='presentation' causes VoiceOver to NOT treat this span as a break.
'role': 'presentation'

Expand Down
5 changes: 5 additions & 0 deletions src/js/control-bar/time-controls/time-divider.js
Expand Up @@ -21,6 +21,11 @@ class TimeDivider extends Component {
return super.createEl('div', {
className: 'vjs-time-control vjs-time-divider',
innerHTML: '<div><span>/</span></div>'
}, {
// this element and its contents can be hidden from assistive techs since
// it is made extraneous by the announcement of the control text
// for the current time and duration displays
'aria-hidden': true

Expand Down

0 comments on commit 8932611

Please sign in to comment.