Accessibility in a Client-side Webapp
Recently I have been rewriting a couple of my iOS apps with Muffin. The UI looks quite nice and the performance is quite good. However, I still need to figure out how to make the webapp work nicely with accessibility tools, esp. iOS’s VoiceOver.
The good news is that VoiceOver can already pick up most texts in the webview. However, the user experience is far from ideal. For example, in the screenshot above, the top left “+” button is read aloud simply as “link”. Since the “+” button is implemented as
<a class='add' href='#'></a>, VoiceOver doesn’t know it should be treated as a button, not to mention an “add” button. A few more problem areas are highlighted in the screenshot.
Luckily all these issues can be fixed very easily. But information on this subject is scarce on the Internet and most articles I could find are quite old. Hopefully this post will make the situation a bit better.
In a client-side webapp, you might have a lot of buttons implemented as links or divs. For example, the “+” button in the screenshot is implemented as:
To tell VoiceOver that this should be treated as a button, all we need to do is to use the WAI-ARIA role and add a
<div class="left-bar-button" role="button" aria-label="add">
Now VoiceOver happily reads the button aloud as “Add Button”. Bingo!
There is a tab bar at the bottom of the screenshot, which is implemented like this:
VoiceOver doesn’t know these are tabs, so it simply reads out the texts. We can do a better job by adding a role to each list item.
selectTab: (tab) ->
When the user changes tab, we set the “aria-selected” attribute to “true” on the active tab. Now VoiceOver reads the tab as “Selected, Favourites, Tab, list start, 1 of 4”. Awesome! That’s much more informative.
In the native iOS app, I have set up accessibility labels for each
UITableViewCell, so that the cell is presented as a whole to the screen reader. For example, in the image above, the selected
BTStopCell is read aloud as “Hotel Grand Pacific, Bus stop #01012”. This helps the visually-impaired to quickly navigate through the list of bus stops.
Adding accessibility labels in the native iOS code is straightforward. This is all it takes:
Now that I have rewritten the native iOS app as a webapp, I need a similar way to add accessibility labels to the stop cells. It turns out to be just as easy.
This is the HTML for the stop cell:
<li class="stop-cell" data-id="<%= stop.id %>">
To tell VoiceOver to treat the cell as a whole, we just need to specify a role on the list item and set its “aria-label”.
<li class="stop-cell" data-id="<%= stop.id %>" role="text" aria-label="<%= 'Bus stop #' + stop.id + ' ' + stop.name %>">
Now VoiceOver reads the whole cell as “Bus stop #01012, Hotel Grand Pacific”, instead of selecting each text in the cell separately.
Hide UI Elements
While playing with VoiceOver, I found that sometimes it picks up a big rectangular image, although I couldn’t see anything there on the screen. It turns out it’s picking up map tiles from a Google Map in a lower layer (the map is used elsewhere in the app but always kept in the DOM for performance reasons). Let’s fix that.
All we need to do is to set the “aria-hidden” attribute to “true” on the map container:
<div class="map" aria-hidden="true"></div>
Sometimes we need to show a toast or an alert message to the user, like in the screenshot below. How can we make this alert message accessible to the visually-impaired?
Quite simple, actually. We just need to add a role to the toast.
<div id="toast" role="alert"></div>
There are a few things to keep in mind, though. You must never set
display: none on the toast element, otherwise it won’t work. I was using
fadeOut() on the toast element before, and it took me several hours to figure out why it’s not working. A simple workaround is to use
showToast: (msg) ->
Sometimes, VoiceOver swallows the alert message because the interface has changed and it has to read out new interface elements. In those cases I have found that adding a small delay (100ms) before showing the alert can help.
There are many more things you can do with WAI-ARIA. Check out the spec for details.