Accessible JavaScript

Created by Marcy Sutton / @marcysutton
Senior Front-End Engineer, Deque Systems

Who is Marcy Sutton?

axe-core Web-A11y Slack
AngularJS Girl Develop It

The old days

  • Flash and keyboard navigation
  • Screen readers and JavaScript
  • Page reloads for data interactions
  • Car phones in Miatas

The Web has evolved. Yay!
How do we code for accessibility now?

What is accessibility?

The Web is fundamentally designed to work for all people, whatever their hardware, software, language, culture, location, or physical or mental ability.

Who are we talking about?

  • Blind & Low Vision
  • Color-Blind
  • Deaf & Hard of Hearing
  • Impaired Mobility
  • Cognitive/Learning

A holistic approach to accessibility
makes the web better for everyone

Disabilities as permament or situational

Web pages are more than
just visible

  • Semantic HTML markup
  • Headings
  • Landmark roles
  • Form labels
  • Image alt text
  • Visually hidden text


Web content is more than
just audible

  • Video/audio captions
  • Transcripts
  • Audio descriptions
  • User interactions

Low contrast
impacts people more than you think

Low contrast footer
Low contrast footer with simulated vision impairment

But you came to hear about
Accessibility & JavaScript

JavaScript Framework Hotness

Let’s talk about:

  • Keyboard events
  • Icon buttons
  • Focus management
  • Testing tools

Creating accessible actions

Empty buttons and links: bad!

  <a ng-href="#/wrong-trousers"></a>

  <button ng-click="start()">
    <i class="icon"></i>
Unique and purposeful link text: good!

  <a ng-href="#/wrong-trousers">Techno-Trousers</a>

  <button ng-click="start()" aria-label="Start Day">
      <i class="icon"></i>

Creating accessible actions Continued

Empty buttons and links: bad!

  <a ng-href="#/wrong-trousers"></a>

  <button ng-click="start()">
    <i class="icon"></i>
Unique and purposeful link text: good!

  <a ng-href="#/wrong-trousers">Techno-Trousers</a>

  <button ng-click="start()">
      <i class="icon" aria-hidden="true"></i>
      <span class="icon-text">Start Day</span>

Button Demo

“Hidden” vs. “Offscreen” Styles

  [hidden] {
    display: none;
    visibility: hidden;


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

WebAIM on invisible content

Keyboard navigation

Any element can receive focus with tabindex="0".

   <div tabindex="0" class="nav-right"
   	role="button" aria-label="Next Slide">

  $('.nav-right').on('click keydown', (event) => {
      if (event.type === 'click' || event.keyCode === 13) {

Use native
buttons and links first.

They come with native ARIA semantics as well as keyboard support.

Skip Links

Skip Links

Useful for everyone. Make them visible on focus:

  li.skip-link {
    display: block;
    margin: 0;
    padding: 0;
    position: absolute;

    a {
      display: block;
      position: absolute;
      left: -10000px;
      top: 0;
      width: 200px;
      &:focus {
        left: 0;
  [tabindex="-1"]:focus {
    outline: none;


      <a href="#main">
        Skip to Main content

  <main id="main" tabindex="-1">


Mega Menus

Mega Menus (fixed)

  • Really hide inactive content
  • Make top level links into toggles
  • Close with escape key

Focus Management

Ensure focus is not dropped

Important for:

  • Client-side rendering
  • Deleting items in a UI
  • Interactive widgets

Focus management: a strategy

 App.FocusManager = class FocusManager {
   constructor() {
     $('body').on('focusin', e => {
       return this.oldFocus = $(;

     App.bind('rendered', e => {
       if (!this.oldFocus) { return; }
       if ('focus-id')) { return this._focusById(); }

   _focusById() {
     let focusId ='focus-id');
     let newFocus = document.querySelector(`#${focusId}`);
     if (newFocus) { return MyApp.focus(newFocus); }

Focus Management

Which item has focus? (debugging utility)

  $('body').on('focusin', function() { 
JavaScript (jQuery)

Testing for Accessibility

We can achieve digital equality by making accessibility part of our web development workflow.

Paul Adam's Bookmarklets

Paul's Bookmarklets page

Open Source Accessibility Engine

  • Browser extensions
  • Unit test integration
  • Selenium Webdriver integration

aXe Chrome extension

aXe Chrome extension

Unit Test Integration

  npm install axe-core

  describe('Form component', function () {
' + '' + '' + '
'); it('should have no accessibility errors', function (done) { var n = document.getElementById('username'); axe.a11yCheck(n, null, function (result) { expect(result.violations.length).toBe(0); done(); }); });

Webdriver Integration

  npm install axe-webdriverjs

  var selenium = require('selenium-webdriver'),
      AxeBuilder = require('axe-webdriverjs');

      describe('Selenium-aXe Tutorial', function() {
        beforeEach(function(done) {
          this.driver = new selenium.Builder()
            .then(function() { done(); });
        afterEach(function() {
        it('Should have no accessibility violations', function(done) {
            .analyze(function(results) {

Learn more on Github

Deque Labs on Github

Should your mobile site
be accessible?

It’s About User Experience.

We can make it better.



Twitter: @marcysutton

Github: @marcysutton