Building accessible-app.com: The shopping cart and aria-live regions

While the last two blog posts revolved around accessibility challenges I found along the way, this post is about the topic I wanted to cover initially - the shopping cart.

I consider this feature actually as one of the core ones of my example app, and it's the feature that defined the specific idea as "Accessibooks", the fake ecommerce app. This had two reasons:

  • ecommerce functionality is very common in today's web-sites and web apps
  • Changing data in an asynchronous way is best explained by either Single Page App (SPA) routing or by the example of a shopping cart that works without page refreshes

To recap, asynchronous content changes are one of the defining things that makes an app an app. If you can see, these content changes are perceivable by you as the user. You interact with something, and as a result something happens - all without a page reload. But if you use other ways to perceive a web page, this changes. When using, for example, a screen reader you consume a web site or app one element at a time. And if your "Virtual cursor" is on a button, and click it, and as a consequence a full page reload does not occur (which would make the screen reader read out the page from its top) - you are kind of lost. You would have to remember how the current page's state and content was before the click, and then actively search for new content. That's unreasonable.

Fortunately there are two ways to help screen reader users:

Going ARIA live

MDN web docs summarizes the situation once again:

Using JavaScript, it is possible to dynamically change parts of a page without requiring the entire page to reload — for instance, to update a list of search results on the fly, or to display a discreet alert or notification which does not require user interaction. While these changes are usually visually apparent to users who can see the page, they may not be obvious to users of assistive technologies. ARIA live regions fill this gap and provide a way to programmatically expose dynamic content changes in a way that can be announced by assistive technologies.

In a nutshell: If you add the aria-live attribute an element (visible or not) it will become a "notification container". Screen readers detect aria-live regions and read their content as soon as it changes. Therefore, you can use this announcement tool to provide hints that are visually apparent to screen reader users - for example, that one particular item has been added to their shopping cart. These regions let web authors "break" the standard screen reader behaviour - that is: reading out a document and its items sequentially from top to bottom. A simple example:

  1. Once your app mounts or your page loads you make sure that an empty(!) element like the following exists (more on the values of the aria-live attribute below)
    <div id="info" aria-live="polite"></div>
  2. Let's image have an online store that has a shopping cart and you have a "Add to cart" button attached to each of your products. On click, this product will be added to the cart. This would be perceivable for sighted users
  3. Your click handler for aforementioned "Add to cart" button could look like this (just for the example only one button is present):

    document.querySelector('.add-to-cart').addEventListener('click', function() {
    // Your cart-adding business logic here
    // ...
    
    // Announcing success with aria-live
    document.getElementById('info').textContent = 'Product was added successfully to your shopping cart';
    });
  4. Then, when a product gets added this is not only visually perceivable but also will be announced by screen readers. Usually, and using aria-live=polite the announcement will wait until the screen reader has finished its current output. In the rare cases you need to report a critical error and cannot wait (read: cannot be polite), use aria-live=assertive. As soon as your live regions' content changes (from empty to not empty, in this case) it will be announced (if you are, like me, not a native English speaker and use the word "assertive" rarely in your everyday life - just look at its first three letters; then you know you got the counterpart to "polite" 😉).

I'm aware this is a very low key introduction to aria-live which only scratched the surface. For details I recommend the page about aria-live regions at MDN web docs.

Accessibooks and vue-announcer

Back to building our example app "Accessibooks" (current implementation: Vue). It would be rather easy to build aria-live regions from scratch in both React and Vue, but luckily there are already components available:

Now that this to do is checked we can reflect on in which situations aria-live announcements make the most sense. In a simplified scenario - and that is what we're building right now - this would be:

  • Adding to or removing a product to the shopping cart from the ProductTable's button
  • In the Shopping cart: deleting one particular product
  • In the Shopping cart: deleting all products

The great thing about the vue-announcer component is that, once you both added it as a plugin and added <vue-announcer /> at the top of your App's template you can "fill it" (therefore sending accessible notifications) from within any component as easy as:

this.$announcer.set("This is the announcement");

This leads to the following code behind, e.g. the "Clear all items" button in the shopping cart:

removeAllItems() {
  store.commit("removeAllShoppingCartItems");
  let message = `Shopping cart is now empty`;
  this.$announcer.set(message);
}

(See this code snippet in context here.)

Using aria-live regions (politely) in this case, screen reader users can now be actively notified of the success of their recent interaction (button click) without having them to search for changes in the document - just as sigthed users can perceive a change in the counter bubble attached to the Shopping Cart menu.

Conclusion

Aria-live regions are a powerful tool and common sense is to use these regions for short notifications only. Wrapping, for example, the whole product table into a live-region would lead to an information overkill and a very verbose output - although there is an aria-atomic attribute that only announces changes in the live region it only makes sense in limited but frequently updated widgets, like a "Who is online" roster or a clock. Aforementioned MDN page has a whole section about advanced aria-live concepts. What you should take from this article:

  • Be aware that your Single Page App's asynchronous nature creates accessibility problems
  • Be aware that programmatically moving focus is not always the best of choices
  • Be aware that aria-live regions and abstractions for Vue and React exist
  • But also be aware that you should not get carried away in their use, and definitely check documentation first

If you want to visit the current VueJS implementation of the accessible app, head to vuejs.accessible-app.com. You can find all of its code on GitHub at github.com/accessible-app/vuejs.