4 Code Snippets to Make Your Site Usable by Assistive Technologies

Soo-Rae Hong
Scribd Data Science & Engineering Blog
9 min readNov 6, 2018

In a previous post, we covered the reasons why it was vital that Scribd, a company intent on changing the way the world reads, make their site conform to the Web Content Accessibility Guidelines (WCAG). The post covered the different software and hardware assistive technologies that are used to browse the web, why things like color contrast, semantic markup, focus management, and aria tags are important, and how our site fell short of all of these capabilities back in 2015. If you’re not entirely sure why it would be important that an app like Scribd (or really any site, for that matter), be usable by assistive technologies, I’d strongly encourage you to read that post.

Did you read it? Great.

As one of the software engineers on the Scribd web team, I’d like to jump right into talking about how we fixed these issues, and the custom Javascript libraries and Sass-CSS mixins we wrote that made it easy to enable different parts of the app to work with screen readers and keyboard navigation systems.

Accessibility is a growing concern of web apps everywhere, and many libraries are being built to make it easier on developers. If you want some plug-and-play solutions (that we haven’t personally tried but seem promising), you might want to check out:

Focusable — a library for focusable HTML

Launchy — an accessible modal window

a11y dialog — customizable native dialog element

If those didn’t solve what you were looking to do, seem like overkill for your needs, or you are just curious to see how we tackled these issues, read on!

1. Creating “visually hidden” labels on non-text elements for Screen Readers.

Like many sites, Scribd uses icons that are intended to be communicate meaning through visual representation. Take this module on our site, for example.

The intent here is that most users would look at the shape in the bottom right corner and be able to intuit that it looks like bookmark and therefore must mean “Save” this book. If they got really stuck, a little tooltip dialog appears on mouse hover that says “Save for Later”.

Of course, Screen Readers don’t have the symbol recognition that most humans do, and they don’t have the ability to trigger hover events if the user is not using a mouse.

So we needed a machine-readable text label on these elements, without making them “visible” on the page and impacting the aesthetic. You might think we could just stick an element on the page that had display: none; or visibility: hidden;, but unfortunately ccreen readers also ignore those elements.

How we did it: We created a .visually_hidden css class that creates an element of 1px height and width but -1px margin, with overflow:hidden; so that it is technically “on the page” but all the inner text is hidden behind the tight borders of the element.

.visually_hidden {  border: 0;  clip: rect(0 0 0 0);  height: 1px;  width: 1px;  margin: -1px;  padding: 0;  overflow: hidden;  position: absolute;}

Now this can be added to any icon, so the screen reader can read a full descriptive text label for every icon, image, and abstract element, while none of it appears on the screen.

(Click on the Result tab in the example below and inspect the icon element to see the visually hidden text).

JSFiddle Link.

2. Announcing Live Changes

Photo by rawpixel on Unsplash

React gives us the awesome ability to swap components and elements out on the fly, based on user interactions without ever having to fully re-render the page. That means our apps are super slick and fast, but it also means that screen readers, who again, don’t have eyes and typically crawl across the DOM once on page load, might miss out on all the flashy updates and whole content changes that are happening right before a user’s eyes.

For example, in our e-reader, as a user turns the page (either through arrow click or progress bar scroll), our e-reader updates the footer text to track reading progress and update pages read, without reloading the page. This goes unnoticed by screen readers.

How we did it: Screen readers use a nifty thing called an aria live region, which is basically an element that has the HTML attribute aria-live=”polite” or aria-live=”assertive”, that are read anytime new text is added to the element.

We added a library with a function called update_polite_live_region that either updates the text in an existing live region on the page, or appends a new aria-live element to the body of the document. (You can have more than one live region on a page and it’s generally recommended to have different ones for different parts of the page.)

Click on the Result tab in the example below. When you click the left or right arrows, the page numbers are dynamically changed. You won’t actually see the “Live Region” since it is “visually hidden” (see #1), but if you inspect the JSFiddle element in devtools, locate a span with the attribute aria-live=”polite" (and class of polite_aria_live_region) appended. Now, with your inspector window open, click on the arrow buttons. You’ll notice that span gets updated with the text. That is what screen readers read aloud to the user.

See it on JSfiddle .

3. Focus Management

Let’s talk about focus.

Or rather, keyboard focus and navigation.

Turns out a good portion of users navigate the web using a series of keyboard commands, not only arrows and tab keys, but also shift +tab for reversing, escape keys for closing pop-ups, and much more.

Natively, most browsers will detect if a user goes into keyboard navigation (usually by hitting the tab key) and then moves focus along all of the anchor links and buttons (i.e. clickable elements) that are on the page. This works decently well on a static page. But very few pages are static nowadays. More often, Javascript makes pages come alive, animating the opening of lightboxes, and menus, and overlaying components upon components through various click events.

With all the custom components opening and closing all over the place, keeping focus inside an “open element” until the user chooses to exit requires a lot of conscious effort on the part of a developer.

To see normal native behavior, go to the Result tab on the JSFiddle below, and using either a keyboard or mouse, click on the “Open Lightbox” button. A Sign In Lightbox will pop up with focus set on the lightbox. Notice how, if you press the tab key, the focus advances chronologically to the next focusable element. Once you get past the “submit” button, the focus has moved on to the next link on the page, which happens to be obscured by the lightbox. If the user wanted to finish filling out the form (or even just close the form), they will have no way of navigating back to do so. You might also notice that using the up or down arrow keys has no effect on navigating within the lightbox form.

Example of default browser behavior without focus trap

JSFiddle Link

Native focus management don’t know what visual effects you’re trying to achieve, so you have to explicitly write code to keep focus cycling in your component (and not escaping to the other links on your page), as well as be able to respond to the keystrokes that assistive technologies are used to using in native browser pop-up windows. And then, after a user closes the component, your code needs to place focus back on the originating element.

How we did it: We created a library with a function that, when we passed in an element, an exit function, and some custom options, would return a function that did a few things:

  1. Takes keyDown events, depending on keyCode, would move focus forwards, backwards, or exit out of the element.
  2. Would keep focus cycling within the element (e.g. when it was on the last focusable object would jump back to the first one advance, or when it was on the first focusable object would jump to the last one if in reverse).
  3. Had an option to keep cycling on the tab key, overriding the “expected” behavior which was to use arrow keys to navigate within an overlaid container and tab to continue moving chronologically. Sometimes, due to design, we prefer tab navigations to also stay within the element.
  4. Would close or exit the element on esc.
  5. Gives the dev control over what elements within the parent are deemed “focusable”. get_focusable_children scans the element for all form elements, anything we manually add tabIndex to, and skips elements that we don’t want to focus on (anything with the classname skip_focus or visually_hidden). See the next section for more on custom focusable elements.

On ComponentDidMount of our Lightbox component, we defined new function this.focus_trap, which was the return value of create_focus_trap(this.lightbox_ref, this.close_lightbox, true). (this.lightbox_ref is the enclosing element for our Lightbox container), and explicitly placed focus on the newly mounted component.

Then, on akeyDown event listener, we attached the focus_trap callback.

Now see the same page in action with focus traps added. You should now be able to use tab, shift + tab and up and down arrows to navigate around the lightbox without worrying about the focus box escaping. When you want to return to the original page, you can use the esc key to close the lightbox.

Example of browser behavior with focus trap

See JSFiddle Link.

4. Not a huge fan of the blue…

This isn’t really an accessibility feature, but more a way to make your UI design work together with accessibility requirements so that everyone’s happy.

Perhaps you’ve noticed that, even with tabIndex, the browser’s native focus highlighter won’t highlight your element. Or, perhaps you have a styled form element (like a checkbox) that visually hides the native checkbox input element with css, and when your user uses keyboard navigation it ends up highlighting an element that is off the screen rather than your nicely styled checkbox. (see more details of this use case here). Or maybe you just hate that fuzzy light blue and think it clashes with your well thought-out brand aesthetic.

That’s where the custom focus ring comes in.

How we fixed it: We put an eventListener on the main container in our page layout. If it detects that the tab key is pressed, it adds akeyboard_focus class on the entire container. This then allows us to use css for a few things.

  1. Anything that has focus within that .keyboard_focus selector gets a custom outline (in our case, a teal 2px solid border), overriding the blue border it would otherwise get.
  2. If a certain element isn’t getting the native :focus, a .focusedclass will apply the same ring around it to maintain consistency with the rest of your component.
  3. A simple keyboard_focus mixin will extend your hover styles, so that keyboard navigation users get the full experience of all your hard UI work without needing to duplicate css.

The following pattern for applying the .keyboard_navigation class can be implemented using jQuery, vanilla javascript, or implemented through a state manager like Redux. We’ve done all of those at Scribd. But at the most basic level, this is how we added our own custom focus ring.

To see it in action, click on the Result tab in the example below, click in the window, and press the tab key (focus ring will be blue for the first focus as the keydown event is detected, but should be custom for every successive focusable element). You can even try it out in the lightbox element.

JSFiddle Link

And there you have it! 4 simple ways to suddenly make your site usable by screen readers and key navigation systems. You’ve just increased your user base and user happiness significantly, and made your site more inclusive and accessible to all. Great job!

If you want to work on changing how the world reads, come join us! www.scribd.com/careers

--

--