Back to blog
7 min read
Lessons Learned from a11y

Background

The purpose of this document is to provide a list of accessibility (or a11y for short) issues that engineers should be cognizant of when developing. A developer can support building accessible websites, often by adhering to the HTML standards; but there are some a11y issues that are not as intuitive or unusual. By providing this lessons learned from a11y documentation, hopefully other engineers will be able to recognize and catch issues early on the development process when they are either writing code, reviewing a PR or dev QAing.

What is VoiceOver?

VoiceOver is the built-in screen reader in Apple OS X and iOS. Its purpose is to provide increased accessibility to blind or low-vision users, as well as users with dyslexia. A a11y issues can stem from elements or sections of the page that the screen reader does not provide enough or sometimes too much information to the user.

Getting Started with VoiceOver

DescriptionKeyboard Shortcut
VO Keycontrol + option
Turn VoiceOver on/off⌘ + F5
Move VoiceOver cursor to next itemVO + Right Arrow
Move VoiceOver cursor to previous itemVO + Left Arrow
Click/push a selected itemVO + Space bar
Interact with an item (drill down)VO + Shift + Down Arrow
Stop interacting with an item (drill up)VO + Shift + Up Arrow
Use Auto Web SpotsVO + ⌘ + N
Open the RotorVO + U
Access VoiceOver settingsVO + ⌘ + Left or Right Arrows
Change VoiceOver settingVO + ⌘ + Up or Down Arrows
Access the DockVO + D
VoiceModifier key (hereafter referred to as VO)CTRL + OPTION or CAPS

VoiceOver Keyboard Shortcuts


Lessons Learned from a11y

Ensure error messages are associated with related inputs: When a user exits an input and error message displays then the screen reader should notify the user of the error.

Recommendation:

The error message should be associated with its related input with aria-describedby referencing a unique id on the message element.

Example:

<input
	id="my-input"
  type="text"
	aria-label="Enter some value"
  placeholder="Enter some value"
	value=""
	aria-describedby="unique-error-widget-id"
/>

<div
  id="unique-error-widget-id"
  className="error"
  data-testid="errorWidget"
>
	Error, you have entered an invalid input.
</div>

Ensure the user is informed of dynamically updating content

With SPA apps, a user action of clicking or inputting data can dynamically change sections of the page. While this visual changes may be apparent to users who can see the page, they are not as obvious to users of assistive technologies. This is where ARIA live attributes help fill this gap and provide a way to programmatically expose dynamic content changes in a way that can be announced by assistive technologies.

For example, if a user inputs a value and the updated content occurs the point of current programmatic focus then we can use ARIA live attributes to notify the user.

The aria-live attribute notify the user if the region receives an update. There are three possible values to pass to aria-live: off, polite, or assertive. The default is off. Normally you should use polite. If a region has a label or text accompanied by the dynamic content then you can use aria-atomic so that the screen reader will read the entirety of that section.

Example: CodeSandBox


SVG and Images

If an image provides valuable content to the experience then the <img/> tag should include a concise description of the image’s purpose in the alt attribute. Else if the image does not provide any meaningful information to the content of the page then you can provide a null alt text (e.g. alt="") to the image.

If you have inline SVGs the same concept should be applied as above. If the SVG does provide valuable content to the experience then add a <title/> element in the SVG and the title should be referenced by an aria-labelledby attribute of the svg element. Else if the SVG does not provide any meaningful information then add a aria-hidden="true" to the SVG element.

SVG Example:

<!-- If an SVG provides valuable content: -->
<svg aria-labelledby="my-svg-title">
	<title id="my-svg-title">My Creative Title</title>
	<!-- ... -->
</svg>

<!-- If an SVG does not provides valuable content: -->
<svg aria-hidden="true">
	<!-- ... -->
</svg>

Ensure content structure is in a linear progression

When positioning elements with text content on page it is important that the structure of the elements are in semantic order: meaning they should read in a logical sequence. For example, when displaying a label with a value they should be composed in readable sequence where the label should be directly followed by its value or vice versa. Otherwise this will confuse screen reader users if the content is read in mish-mash order. Let’s first take example of what you should not do:

 <!-- ❌ WRONG ❌ -->
<div class="labels">
  <p class="your-progress-text">
    Your Progress:
  </p>

  <p class="next-checkpoint-text">
    Next Checkpoint:
  </p>
</div>

<div class="stat-values">
  <p class="your-progress-distance">
    6.12 miles
  </p>

  <p class="next-checkpoint-distance">
    10 miles
  </p>
</div>

The above example will be announced by screen reader as: “Your Progress: Next Checkpoint: 6.12 miles 10 miles.” This is not the linear flow where it will confuse screen reader users. Instead it should be structured like so:

<!-- ✅ CORRECT ✅ -->
<div>
  <p class="your-progress-text">
    Your Progress:
  </p>

  <p class="next-checkpoint-text">
    6.12 miles
  </p>
</div>

<div>
  <p>
    Next Checkpoint:
  </p>

  <p>
    10 miles
  </p>
</div>

Now it will be read as: “Your Progress: 6.12 miles Next Checkpoint: 10 miles.”

Summary

Sight users have the ability to scan the screen almost instantaneously, allowing them to quickly comprehend the layout and design of the page content. While screen reader users cannot discern this level of detail. So it is important that the structure of you code is in linear progression where it can be read from beginning to end.


Progress Bar VO

Issue: When using role="progressbar" to create a progress bar there are discrepancies between browsers, OS, and AT/screen reader software. See this issue here of some of the problems that one may incur with VO of different screen readers, browsers, and OS. These variations are likely due to bugs of the screen reader software and/or browsers.

According to W3C recommendation here for progress bars:

The author SHOULD supply values for aria-valuenow, aria-valuemin, and aria-valuemax, unless the value is indeterminate, in which case the author SHOULD omit the aria-valuenow attribute.

and

Assistive technologies generally will render the value of aria-valuenow as a percent of a range between the value of aria-valuemin and aria-valuemax, unless aria-valuetext is specified. It is best to set the values for aria-valuemin, aria-valuemax, and aria-valuenow in a manner that is appropriate for this calculation.

With a crude fix of trying to avoid issues outlined here, we can implement the following, trying to follow the well-documented patterns from MDN web docs here and W3C recommendation here. But there still may be some inconsistency and duplication of text being read depending on the AT/screen reader and browser.

<div
  className="progress"
>
  <div
    className="progress-bar"
	  aria-valuemax={100}
	  aria-valuemin={0}
	  aria-valuenow={round(value, 2)}
	  aria-valuetext={`${round(value, 2)}% Complete`}
	  role="progressbar"
	  aria-label="Progress bar"
  />
</div>

CodeSandBox

Resources: