Browse files

fix(a11y): current time and duration display accessibility with Voice…

…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 893261186b23f3dc196431d182fb970681837e42
@@ -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
@@ -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'

@@ -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

0 comments on commit 8932611

Please sign in to comment.