Screen Readers 101 For Front-End Developers (Mac)

This post was born when, while writing for another post, I found myself repeatedly trying to explain and demonstrate how the examples I’ve showed, affect screen reader users. It made me realize that many front-end developers have a blind spot of an entire aspect of the interface they are working on. To some extent, it is like developing a user interface without looking at the screen. Today’s screen readers are available for all operating systems, and most (if not all) modern OSs have a built-in screen reader. I therefore thought it would be a good idea to write a short guide to essential screen reader usage to help front end developers adding it as an additional item in their self-test tool belt. In this post, I will refer to MacOs VoiceOver. If you are a Windows user, check out the Windows version of this post here.

This post is by no means intended to be a comprehensive user guide or for making you screen-readers experts, but merely to give you a tool that lets you peek into another layer of the UI you are delivering. It may affect many users’ user experience.

We will cover in a nutshell what screen readers are and how they work, as well as a  few basic concepts for using them, mainly how to check that assistive technologies are conveying all the interface components as intended. 

What are screen readers and how do they work?

A screen reader is a form of assistive technology (usually a software) used mostly (but not only) by people with vision impairments. 

Screen readers provide audio and braille output, and most of them also have visual indicators.

Screen readers use the operating system’s accessibility APIs to parse and read the UI to the user. The problem is that each OS provides its specific accessibility API, and sure enough, you can’t find a cross-OS screen reader. 

When it comes to the web, things get even more complicated, since every browser has its accessibility API, which screen readers use to read the web page alongside the OS’s accessibility API. This assortment of APIs causes inconsistency and differences with how screen readers read the web page and support the WAI-ARIA attributes on different browsers; therefore, each screen reader has its recommended browser for maximum compatibility. In this post, we will discuss Apple’s VoiceOver. Safari is its recommended browser; nevertheless, I usually use VoiceOver with Google Chrome, and in almost all cases, it works as expected.

For reading web pages and web applications, screen readers use the “accessibility tree”, which is somewhat parallel to the DOM tree with the addition of accessibility features, such as the definition of accessible-names and mapping of widgets states.

Chrome devtools, accessibility tree view
Chrome devtools, A11y tree view

Now that we understand what screen readers are and which challenges it brings, let’s start working with it and see how it works.

Installation and settings

Mac users are lucky as they are free from any download/installation process, since VoiceOver is part of the OS X bundle. They can therefore start by turning VoiceOver on by pressing “command + F5″ (the same key combination also turns it off).

Another option is to open System Preferences > Accessibility > VoiceOver, and check the “Enable VoiceOver” checkbox.

Mac OS, accessibility settings window, VoiceOver

When I first opened a screen reader, it was somewhat overwhelming. In retrospect, I can say that one of the reasons for this was the screen reader’s speech rate, which was a bit fast for me. I also felt I was missing important information. Therefore, if you too feel a little overwhelmed, you can adjust the speech rate by getting to the VoiceOver setting as shown above, and clicking the “Open VoiceOver Utility” button on the bottom of the window.

Mac OS, accessibility settings window, open VoiceOver utility settings

Here you can set the preferences of VoiceOver, add languages, change the narration voice and style, set the output type (auditory, braille, or both), and more. We are here now to adjust the speech rate.

VoiceOver settings, speach, set speech rate.

On the new window that is open now, click on the “Speech” option on the left side and change the rate column’s speech rate on the right. A lower value slows the speech pace. The system will read you a sample text each time you change the rate value to get it to the point where it feels comfortable and less overwhelming. This is as much as we are going to dive into the VoiceOver settings, but it is all pretty straightforward. 

Next, we will discuss some VoiceOver terminology.

Basic Terminology

In this section we will overview a few essential terms that VoiceOver uses and the concepts behind them.

VO Keys

VoiceOver uses the control + option keys combination to take over and control the keyboard events. This key combination is usually referred to as the VO keys. However, almost every action you wish to take with VoiceOver will require this key combination. VoiceOver will give you instruction while using it and guide you when to use the VO keys and other required key combinations.  You can lock (and unlock) the VO keys, so you don’t need to hold them by pressing control + option + ;.

VO Cursor

Since screen readers are not only used by people with vision impairments, VoiceOver, besides the auditory output, also has a visual indicator, called the VoiceOver cursor. The VO cursor is a black frame that indicates which piece of the UI the screen reader is currently reading. As a user accustomed to referring to the UI in its visual context, the VO cursor is very handy, as it makes the connection between the visual and auditory UI. As a front-end developer, the VO cursor can help you understand how the screen reader conveys and outputs each of the DOM elements to the user.

VoiceOver's visual cursor.
The VO Cursor

The VO Rotor

The VoiceOver rotor is a rotating menu that allows users to quickly access elements by their types, like headings, links, and landmarks. For us, as front-end developers, it provides a quick look on how VoiceOver is reading the elements of the UI. The VO rotor will therefore also be at the center of the examples I will bring later in this post.

To turn on the VO rotor, press “control + option + u.” You should be able to see the rotor modal and be able to page through the different elements’ types, using the right and left arrow keys. To navigate between elements on each list, use the up and down keys (there is no need to keep holding the VO keys in both cases).

Next, we will look at a few of the VO rotor’s different lists and see how we can detect accessibility issues with them, and also better understand the user experience of screen reader users.

Quick Accessibility Overview with VoiceOver

For those of you who would like to follow along – the examples I have added in the bottom of each section link (Try it yourself) to the pages that are used on each example.

Headings

Let’s start by looking at the headings list, and see what we can learn there. (Remember, you can get to it by using the Right and Left arrows when the rotor is opened.) 

The headings structure and hierarchy in a web page should act as its table of contents; thus, the user can always understand the context of the content piece that the screen reader is currently reading. It also allows quick access to the exact part of the content that the user is interested in, without making them go linearly through all the page content until they get to the piece of the content they wanted to get.

VoiceOver rotor, headings list on a Wikipedia page.

See the example above. It shows the heading hierarchy of a Wikipedia page. The headings are ordered by their position on the DOM, the heading levels are in an ascendant order and each level represents a sub topic of the heading above it. You can see that the headings hierarchy and order on the rotor modal window match the table of contents of the page. This is a perfect example of a suitable headings hierarchy that allows the user to get a good mental image of the page’s content hierarchy. You can filter the headings by their level by typing a number between 1-6, or by their content by typing their internal text.

Signs to possible headings related a11y issues

  1. The headings list is empty: Headings are essential for screen reader users to get acquainted with the page and understand its overall and each part’s context. If the headings list is empty, ask yourself if a user, who cannot see, has the information available to get acquainted with the UI and to get a full mental image of it.
  2. Headings levels are inconsistent and not in an ascending order: Inconsistent use of headings levels causes a lack of clarity of the UI structure. It fails to imply the importance of each of the different content pieces for screen reader users.

Landmarks

Landmark elements are HTML elements that have an explicit role in the UI structure, such as <nav />, <main />, <header />, and <footer />; the landmarks list should represent the different components of the UI’s layout. To better understand how the usage of these elements on the screen reader users’ experience, let’s look at an example for how landmarks are reflected on the VO rotor.

First, let’s look at a page that represents the ideal DOM structure. It is built with semantic landmark elements and each of the landmarks is properly labelled.

VoiceOver rotor, landmarks list.
Try it yourself

In the image above, you can see the list of the landmark elements; the non-unique elements, such as <nav /> and <section /> (regions), are labeled with an accessible name, so the user can grasp their context. To label the elements, I used the “aria-label” attribute.

Now, let’s look at the same page with a slight change. I have removed the accessible names from the non-unique elements, so let’s see how it affects the screen reader users’ user experience.

VoiceOver rotor, landmarks list. Elements has no accessible names
Try it yourself

The image above shows how the landmark list looks when the landmark elements are not labeled. 

First, you can see that we still have three navigation elements, but now we can’t tell or conclude the purpose of each of them. 

Second, all the <section /> elements are not there anymore, but the markup itself did not change at all. The only change I made was to remove the “aria-label” attributes; however, when a <section /> element is not labeled, it is considered by VoiceOver as a generic container – just like a <div /> element. Therefore, it omits it from the landmark menu. Nevertheless, <section /> elements will still be registered to the accessibility tree by accessibility APIs with their semantic role. 

I think this example clearly illustrates the vast difference in user experience when elements are labeled with an accessible name to hint the element’s content and purpose. Now, let’s take it one step further and see what the landmarks list displays if we choose to use only <div />s for our UI.

VoiceOver rotor, landmarks list. divs based layout
Try it yourself

Now, we do not even have the option to quickly navigate to a <nav /> element and hope that this is the element we are looking for; now the Landmarks list is replaced with a “Loading more items…” message. In some cases the list will not show up at all if it has nothing to display (other rotor lists will still show if they have content to display). 

Signs to possible landmarks related a11y issues

  1. The landmarks menu is empty.
  2. Items on the landmarks menus have no accessible names (not labeled).

Links

The links list shows a list of the links on the page, ordered by their order in the DOM. Let’s see what we can learn from this list.

VoiceOver rotor, links list

I have scrolled down the links list on the example above to show the page’s bottom part links. On its top, you can see the last links of the main navigation. They all have a unique name, and the purpose of each of them is clear. So far, so good. Right after the navigation links, you will see a bunch of links that all have the same name, “Read More”. As you can see, they are coming from the items behind the rotor’s modal window. In this case, the words “Read More” are meaningless when disconnected from their context. Links must have unique names, so their context and purpose are clear to all users. If you have to use a generic text on a list of link elements due to UI design constraints, you can add to them an “aria-label” attribute for invisible labeling. Note that different links can have an identical name provided they have the same URL address.

Let’s now discuss the last three items on the links list. They are showing the three icon links on the footer. These links have no accessible name, so VoiceOver is doing its best to provide the user with some names. In such cases, VoiceOver uses the last part of the link’s URL, but since URLs often has a query string attached to them or when the URL name is based on the page ID, the name that’s eventually read to the user is not descriptive nor does it have meaning for the user.

Signs to possible links related a11y issues

  1. The same link name is repeated multiple times.
  2. Links names are not meaningful, or not describing the link’s destination correctly.

Form controls

The form controls list is a critical one. It displays the type of elements that requires user input, such as <button />s, <input /> elements, and <textarea />s. Therefore, problems found in this list may indicate that some users are unable to perform basic operations on the site. 

Let’s look at two necessary conditions for a user to be able to interact properly with UI widgets.

  1. How to use it: The users must understand how they should interact with it; thus, they must understand what the element’s type is. Different input elements may require different types of interaction (typing or clicking, for example).
  2. The outcome: The second condition that must be met is that the user will be able to assess the result of interacting with the element. The information that is supposed to imply this to the user is the element’s accessible name; therefore, this type of element must be labeled correctly.

Let’s see how this is reflected in the form controls list to get an idea of how screen reader users are affected by it.

VoiceOver rotor, form controls list
Try it yourself

The image above shows you how an ideal page looks.  On the rotor window, you can see how each of the elements on the list has an explicit type and name. The user therefore has enough information to quickly understand how they should interact with each element, and what its purpose is.

I’ve made some changes to the page that the average user will not notice since the page’s functionality has allegedly not changed. However, for screen reader users, the form on the page has become unusable. I have replaced the form’s <label />  tags with <span />s and the <button /> tag of the “submit button” is replaced with an <a /> tag.  Let’s see how the form controls list looks now.

VoiceOver rotor, form controls list. Form fields has no accessible names

The first two items are the search input and button from the page header and these stay  unchanged. 

The next item on the list looks like it has a name: “My Name”, but if you will look at the image, you can see that this is actually the input value, I put it there to emphasize that the text on each field which looks like a placeholder is actually the label element (and on this example span).

Right below it the next three empty inputs show as “edit text” and the same goes for the three radio buttons. You can see how a screen reader user just can’t know what kind of data they should type to each input or what the purpose of the radio buttons is.

Next we have a <textarea> element, which looks like it has an accessible name “Write us something”. It also looks that, unlike the name input, it doesn’t have any value typed into it, so where does it get its accessible name from? Something that the <textarea> does have on this example, and which the other <input /> element doesn’t, is a “placeholder” attribute. I brought this example because not all screen readers support this attribute as an accessible name, so even though it works perfectly with VoiceOver, it is not recommended to rely on it as an alternative to other labeling methods.

Next, is the checkbox that has the same issue like the radio buttons, and lastly, the submit button is missing.;You will remember that the button was replaced with an anchor tag, all its functionality stayed the same and the same event listener is attached to it and still the VoiceOver is not classifying it as a button. From this we can learn that when the semantic role of elements is incorrect the user may have difficulty finding them in the places he expects and to interact with them correctly.

Signs to possible form fields related a11y issues

  1. Form fields have no unique names (make sure that the names appearing on the rotor are not coming from values that were typed by the user or from a placeholder attribute).
  2. Elements you expect to find on the list are missing from it.

Articles

For the final list I would like to discuss, I will dedicate just a few words; the articles list displays all the DOM elements that VoicOver can read. For example, you can use this list if you expect to find a certain element on another list but can’t find it there. You can filter items on the list by typing their content or name.

Summary

The examples I have given here in no way represent a complete accessibility test. Still, they indeed show how you can get a good idea of what the initial experience of screen reader users from the interface will be at a glance.

To summarize, I hope you find this guide useful and that it will help you make screen readers an additional item in your tool belt and the efforts for a more inclusive web.

If you wish to learn about VoiceOver in more depth, check out Apple’s VoiceOver user guide and the Commands and Gestures table.

Thank you for reading!