Accessibility Gotchas at Duo
In the past few months, we’ve been working on making our Duo Authentication Prompt more accessible to our users. We’re aligning to the Web Content Accessibility Guidelines (WCAG) 2.0, the most widely used set of standards for creating accessible websites. It’s based on 4 principles: that sites are perceivable, operable, understandable and robust. There’s various success criteria for these principles, and specific techniques that can be used to meet them. Many of the techniques are straightforward and simple, but there were some problems we ran into where there was not a clear answer, and we needed to get creative.
Let’s look at a few examples of this from the enrollment process in the Duo Authentication Prompt.
Screen-Reader-Only Content as Alternatives to Visual Cues
Sometimes frontend devs use icons and animations to convey information on the screen, and these can be really great for sighted users. But they don’t contain information that’s digestible to blind and low-vision users. These users might use a screen reader to read content from a screen aloud. Some screen readers also include magnification tools to make content more visually accessible. Commonly used screen readers are VoiceOver on Macs or the NonVisual Desktop Access (NVDA) screen reader for Windows.
Icons and animations contain information that may not be accessible by screen readers. An example of this is our green checkmark icon that shows up when a verification code is verified.
If the code is incorrect, we show a red X:
This is pretty simple for a sighted user to understand, but unfortunately, this type of feedback is unavailable to a visually impaired person using a screen reader. Nothing else on the screen indicates that the code is right other than the icon. So, in addition to animating the checkmark, we added help text just for screen readers that would give them the same information. Here’s what we did:
We added a span for the screen reader text that is also an Accessible Rich Internet Applications (ARIA) live region:
A bit about ARIA live regions - in the past, screen readers could only know about content that was present in the DOM on page load. This meant that they would miss any content from a dynamic change on the page. ARIA live regions were created to fulfill this need. Setting one on an element lets screen readers know to watch for updates to that element; they can then let users know about the changes.
Setting the aria-live
attribute to polite
turns the live region on. polite
means that screen reader should wait until the user finishes their current task (e.g. typing into a field) before reading updates. This is the more commonly used setting for live regions. assertive
can also be used, but it will interrupt the user, so it should be used sparingly as it creates a more jarring experience. The default setting is off
.
Going back to our screen reader text, all we need to do is set the text of the span to a message at the same time that we show the checkmark or X.
A caveat to live regions is that they can only tell screen readers about elements that are rendered on the screen. But ideally we’d like to hide the messages for sighted users, since they already have the checkmark for feedback. Styling the element with display: none
or visibility: hidden
will hide the element from screen readers completely, so that won’t work for us.
There are a plethora of ways to hide text visually, one of the most widely talked about being the -9999px method. But for that method, browsers actually draw a 9999px box around the hidden content. Because many users access the Duo Authentication Prompt from mobile devices, this is a performance concern for us. There was also the clip method, but the clip CSS property is now deprecated, and its successor, clip-path, is not supported in IE and Edge, both of which we support.
This is what ended up working for us:
For text that we want to be visible to screen readers only, we add this class. This works because:
- All of the text outside the 1x1 pixel is clipped by
overflow: hidden
- The pixel is rendered on the screen, so screen readers have access to its text
- There is no background color, so sighted users don’t see anything
-
position: absolute
removes it from the flow of the page so it doesn’t affect other elements
So by updating a live region with a message and hiding it visually, we created a way for us to give screen reader users a text version of visual information we had on the screen.
Headings vs. Legends?
The Duo Authentication Prompt is an embeddable iframe
in which each page usually has one form element:
From WCAG 2.0, we learned that it’s important for forms to have fieldsets with legends for related controls. For example, on the form above, we could group the controls by a fieldset with a “Phone number” legend.
It’s also important for each page to have an <h1>
heading, and for all the headings on the page to be nested (e.g., h1 is followed by h2, h2 is followed by h2 or h3, h3 is followed by h3 or h4). This makes wayfinding easier for screen reader users - they can quickly skip through all the headings on a page to get a sense of what’s there.
So what happens when our page only contains one form? Originally we thought it would be more important for the form elements to be connected, and we didn’t want to repeat information, like having a heading that said “Enter your phone number” as well as a legend, “Phone number” right under it.
But it turns out that it’s important to have both. Both the headings and the legends make the page structure available to assistive technologies and they have different purposes - one is for wayfinding when the page first loads and one is to make forms more understandable. Neither is more important than the other in this case.
Our solution was to keep the headings on the page as they were, and add legends to each form. We hid the legends with the screen-reader-text
class above so that our tiny page wasn’t overwhelmed with text, and it was still accessible to screen readers.
Page Navigation Inside an iframe
Another important thing for accessibility is to notify users during navigation to a new page. During normal page navigation, screen readers will read the text content of the new page’s <title>
element. But when navigation happens inside of an iframe
, like in the Duo Authentication Prompt, screen reader behavior is inconsistent. Some software, like VoiceOver, will read the <title>
element on the HTML inside the iframe
; others like NVDA will say nothing.
To solve this problem, we brought focus to the headings that we have on each page:
Setting the <h1>
with a negative tabindex
allows us to programmatically set focus to it (<h1>
is an element that normally can not receive focus). On every page load, we call this function, and screen readers will be able to announce what the new page is.
The last thing we did was to style the <h1>
with outline: none
to hide the visible focus, and we’re done.
What Did We Learn?
Retro-fitting accessibility into an already existing product can be tricky, but when there’s not a clear path to a solution, it’s okay (and fun) to get creative. It’s important for us web developers to design great user experiences for all of our users out there, and it’s never too late to start making positive change.
If you’re new to web accessibility, check out our first blog post on the topic, with some sweet resources to get you started.