Video.js Blog

Brandon Casey2017-02-03

Feature Spotlight: Accessibility

Accessibility! The most important feature you never knew about.

In the Video.js organization we try hard to have good accessibility. Like most other software, any change can affect the system in unintended ways. For example MuteToggle and VolumeControl were married into VolumeMenuButton in Video.js 5. While this change did allow these controls to work in tandem visually, it also did something unintended. It broke accessibility. In this post we will go over what broke, what the fix was, what accessibility is, and how to test and make sure it works.

Feel free to skip to the last section if you already know what accessibility is.

Accessibility? What's that?

Accessible software has support for users with vision, hearing, movement/dexterity, or other impairments. It also helps users that want to use the keyboard to navigate. Out of the box web applications have some accessibility due to the nature of HTML, but this is only the case if you are using native elements in intended ways. If you cannot use native DOM elements, like <button>, and instead must use a <div> for buttons, then you need worry about accessibility in your page.

Supporting users with hearing impairment is not something that we can do directly for the users of Video.js. Instead we must indirectly support these users by adding support for captions in videos. In Video.js we have had support for captions and subtitles for some time, internally they are called TextTracks. In fact Video.js has had support for WebVTT format TextTrack, which is much more accessible, since version 4.

Supporting users with vision impairment is harder, but partly in our control. To support this group of users our player must be accessible to screen readers. A screen reader is an application that reads elements off of the screen to the user (as the name implies). On top of reading from the screen it also allows the user to interact with the page using only the keyboard or specific gestures on a touchscreen (without using a mouse or needing to directly touch visible items). HTML has certain rules that must be followed so that a page can be accessible. We will go over the basics of these rules in the next section. Screen readers are further supported by having description tracks that can be read out during video playback. Description tracks are a sub-type of TextTrack, and as previously stated we cannot automatically add them to videos, we can only have support for them in the Video.js.

See the resources section at the end of this post for a list of screen readers.

How do you make a web application screen reader accessible?

If you use the native elements for the purposes that they were intended, you will already have most of the work done.This is why the use of the native element is the recommended way to make anything accessible for a screen reader. For instance if you use a <button> element you will get the following accessibility attributes (without them actually being on the button):

  • tabIndex which allows users to tab to the button
  • role="button" which tells the screen reader that this is a button
  • The space and the enter key will both press the button

In some cases, such as in Video.js, it will not be possible to use the native <button> element. You will have to mimic the accessible functionality from the list above and use a div. Here is a list of what you will have to add:

  • You have to add the role="button" attribute to classify it as a button.
  • You have to add a tabIndex which will allow the div to be navigated to using the tab key
  • You have to add handling for the space and enter key that press the button

A list of role attribute values can be found on Mozilla Developer Network.

After mimicking or adding native accessibility on the controls and content in your webpage, the next thing to look over are aria attributes. For instance, we use aria-live="polite" for our ProgressBar slider. By default aria-live is set off, which means updates to controls should not be read to the user unless they un-focus and re-focus an element. The value of polite which we use allows us to convey the position of the slider to screen reader without them having to change focus on the control. This is useful because the ProgressBar is always updating while a video is playing. A value of polite will also wait to convey said updates until the screen reader is done reading other information to the user.

For a more complete list of ARIA attributes see the specification.

Finally you need to add an accessible "name" to an element so that it can be referred to. A good example of this is can be seen in the MuteToggle control. Since it is not a simple "button" we include innerHTML/innerText of "Mute" or "Unmute" in a way that is hidden from most users but announced to screen readers. In Video.js we refer to the accessible name and the action that a control performs as "control text". Control text also updates the title attribute of an element in most cases, which is important for visual accessibility. When the action the a control performs changes so does the control text. This will allow the screen reader to refer to the MuteToggle as "Mute Toggle" rather than "button". It will also convey the current action of the MuteToggle. In this case that will be either "Mute" or "Unmute" depending on what the button would do when pressed (ie the state of the button).

Here are some examples of accessibility straight from Video.js:

  • The MuteToggle <button>:
    • Has aria-live set to polite, rather than the default value of off. aria-live with any value other than off indicates that innerText/innerHTML updates can be sent to the screen reader without the user needing to move focus off of the control. The value of polite means that the screen reader should wait until it is done speaking to convey these updates to the user.
    • Has control text of "Mute" or "Unmute" which indicates the current status of the button to the use
  • The VolumeBar slider <div>:
    • Has a role attribute with a value of slider. Like this: role="slider"
    • Has a tabIndex attribute as it is not a native control element
    • Has EventHandlers that listen for:
      • The up and right arrow keys to increase the volume and the slider percentage
      • The down and left arrow keys to decrease the volume and the slider percentage
    • Has aria-label of "volume level" which is an accessible label that the screen reader will use to refer to it
    • Has aria-valuenow and aria-valuetext properties that update to indicate the current volume level (so the screen reader can read it)
    • Has aria-live set to polite, rather than the default value of off. aria-live with any value other than off indicates that innerText/innerHTML updates can be sent to the screen reader without the user needing to move focus off of the control. The value of polite means that the screen reader should wait until it is done speaking to convey these updates to the user.

The problem and the solution

Now let's talk about how screen reader accessibility broke in Video.js 5. First VolumeMenuButton replaced MuteToggle and VolumeControl on the ControlBar. VolumeMenuButton was set to mimic MuteToggle when clicked. It would also show the VolumeControl on mouseover or focus. This was a problem because a VolumeControl was a now a child of a button, and buttons should not contain other controls. To the screen reader and to the DOM there are two MuteToggle button controls. When visually there is a VolumeControl and a MuteToggle. Below you can see a gif of this behavior in action :

macOS `VoiceOver` Before The FixmacOS `VoiceOver` Before The Fix

The solution to this problem was to use a regular div to house the MuteToggle and VolumeControl. This regular div would have no role or control text so that it would be invisible to a screen reader. From that point forward we just needed to mimic the old UI. For those who are wondering, this new Component is called the VolumePanel. See the new behavior in a gif below:

macOS `VoiceOver` After The FixmacOS `VoiceOver` After The Fix


Another big accessibility fix for controls comes from the removal of one small css rule:

outline: none;

Why did we do it? With feedback from the community and external resources, we learned that outlines should always be on. Without outlines there is no visual indication of keyboard focus on control elements and without that, keyboard users who are not visually impaired have a hard time using the controls.

Wrap up

Hopefully this post has given you some insight into making a web application accessible. If you find any issues or have any suggestions for our accessibility or in general feel free to contribute to Video.js.

If you want to keep up to date on the current state of accessibility work see the a11y label on PRs and issues.


Here are some popular screen readers that are actually used in the wild:

Resources for learning more about web accessibility: