Web Accessibility for Javascript Developers

Who am I?

Andrew Macpherson

Backend developer at Annertech

Contributor at Drupal project

Twitter: @martianwebdev
Github: fuzzbomb

Why accessibility

Accessibility Principles

W3C Web Acessibility Initiative (WAI)

Web Content Accessibility Guidelines 2.0 (WCAG)

  • Perceivable
  • Operable
  • Understandable
  • Robust


WCAG Success Criteria

WCAG is organized as a set of success criteria, each describing an accessibility practice in terms of:

  • Intent & Benefits
  • Priority level (A, AA, AAA)
  • Examples
  • Sufficient techniques
  • Common failures

Example: SC 1.3.1 Info and Relationships describes the use of headings and labels to indicate the structure of a page.

Things I don't want to cover in depth today

... because this talk is for JS developers!

... because there isn't time,

... but these things are still important topics.

Automated a11y testing

  • It's a HUGE topic.
  • Lots of exciting recent developments, including DOM and headless browser testing.
  • Many things can't be tested easily or reliably. Automated testing is a adjunct to human review.
  • Tons of reports - sometimes this doesn't really help you understand the whole accessible experience.

Content Author's Contribution

Note: content authors/editor's decisions are just as important as designers and developers.

  • Document outline, headings, sections, etc.
  • Organising content, writing styles, use of diagrams, plain English, etc.
  • Context - choice of text alternatives for images.
  • Audio/Video content, captioning, etc.


  • TL;DR - 2010 Equality Act basically says "don't discriminate".
  • When focusing on compliance, sometimes you're being bureaurocratic instead of thinking about the accessible experience practically/holistically.
  • Commissioning guides, and thinking about accessibility at organizational level - see BS 8878

Role of Design, IA, and MAinstream UX

Actually, I love talking about this. Just not today.

  • Colour Contrast
  • Colour Blindness affects ~4% of population!
  • Windows high-contrast mode.

Example UI widget

Let's start with an inaccessible example,
then see how we can improve it.

Screenshot of a mobile menu icon.

We'll make a mobile menu button - a.k.a "Burger Menu"

Sample Code

Git repo: fuzzbomb/mobile-menu-a11y-demo

Improvements made during these slides are marked as git tags, which we'll refer to as we go along.

A working demo of the finished menu widget is also available.

HTML markup

Our button is a <div>, and our menu is a list of links.


Javascript Behaviour

Bind a click handler to our <div> to toggle menu visibility.

    $(document).ready(function() {
      // Initialize.
      var menuDisplayed = false;
      $('#main-menu nav').hide();
      // Add the open/close behaviour.
      $('#main-menu-button').click(function(event) {
        if (menuDisplayed === true) {
          $('#main-menu nav').hide();
          menuDisplayed = false;
        else {
          $('#main-menu nav').show();
          menuDisplayed = true;


"Works with a mouse"

Git tag: milestone-start-works-with-mouse (dbd95a5)

The Keyboard-only Experience

Just a few simple controls.

Not necessarily using assistive-technology
Might be a plain-old QWERTY keyboard.

Basic keyboard controls

  • TAB to move between interactive elements.
    (SHIFT + TAB moves backwards among them.)
  • ENTER and/or SPACE to operate interactive elements.
  • Arrow keys help in some places (radios, select, slider)
  • ESCAPE cancels things (e.g. dialogs).
  • Navigate viewport with SPACE, PgUp, PgDn, Home, End
  • Browser Menu Bar + Context menus
  • Caret browsing, spatial navigation - some browsers only.

Focus styles

Sighted keyboard users really need to be sure what element has focus, before they press enter.

Subtle focus styles don't work - they need to be clear. Beware of re-using mouse hover styles.

This doesn't limit your creativity - take inspiration from games consoles and Smart TV.

Bypass Blocks - part 1

"skip to main content" links

Allows quick access to interact with the page's main content, without traversing lots of sidebar links, or a header menu.

Who benefits? Primarily sighted keyboard users. (Screen-readers offer other ways to get around a page.)

WCAG Success Criterion 2.4.1 - Bypass Blocks


Myth-busting - "Off-Canvas"

Off-canvas != Off-viewport

Be careful. Elements rendered off-viewport can be
a help or a hindrance...

"Invisible" labels are still accessible to screen-readers.

Focusable elements are traversed by sighted keybpoard users, who can't tell what is in focus.

Fixing the keyboard-only experience

The menu widget we have built does not work for keyboard users at all. The expected behaviour is:

  1. Press TAB until I can see the button has focus.
  2. Press space or enter to activate the button.

Allow keyboard focus


Add a tabindex=0 attribute to our <div>.

Keyboard focus is now possible, but the button
does not yet respond to keypress.

Indicate the focus

    #main-menu-button {
      background-color: #444444;
      color: #ffffff;

    #main-menu-button:focus {
      background-color: #ffffff;
      color: #444444;

Use CSS to make it very obvious that the button has focus.

In this example, we invert the colour contrast.
Avoid styles which are too subtle.

Respond to keypress

        .keypress( function(event) {
          // Only respond to enter or spacebar keys
          if (event.which == 13 || event.which == 32) {
            // Spacebar usually triggers page scrolling.
        .click(function(event) {
          // The open/close behaviour
          // ...

Listen for keypress event, confirm which button was pressed, and trigger a click() event.


"Works with a keyboard"

Git tag: milestone-works-with-mouse-and-keyboard (3b8622c)

The Screen-reader Experience

Whoah. Learning curve.

Two Crucial Tools

  1. Headphones!
  2. The stop-talking key command.

Be kind to your colleagues :-)

Slice & Dice

Many ways to navigate or inspect a page...

  • Document outline: sensible heading levels!
  • ARIA Landmark Roles: header, footer, nav-bar, search box.
  • Focus via TAB key, like a sighted keyboard user.
  • Forms mode (some screenreaders offer extra forms tools).
  • Browse a list of links.

Screenreader User habits

WebAIM Screenreader user survey

  • What technology do you use?
  • Preferred navigation methods?
  • Dealing with images, frames, PDF, multimedia, ...

Bypass Blocks - Part 2

ARIA Landmark Roles

<ul role=navigation>

main, banner, contentinfo, navigation,
complementary, search, ...

New HTML5 elements - main, header, footer, nav, aside.


Semantics Revisited

Re-cap. We already know this stuff...

HTML Elements

  • <div> means nothing>
  • <span> means nothing>
  • <button> - press it, makes something happen.
  • <a href> - hyperlinks direct you elsewhere.
  • Button vs Link. We'll run into that a lot.

Human-readable semantics

Attributes which often contain rich semantics, but are really only useful to a human audience: developers!

  • class attribute
  • data-* attributes
  • ng-* attributes
  • Others?

None are understood by screen-readers.

Semantics for screen-readers

We want machine-readable semantics

  • HTML Elements: headings, tables, forms, ...
  • HTML attributes: disabled, required, checked, ...
  • aria-* attributes
  • SVG, MathML.

Introducing WAI-ARIA

HTML is NOT a user interface language.

ARIA provides vocabulary of UI roles, states, and properties.

They express the range of interaction found in native Desktop and Mobile UI widget APIs.

Formal, machine-readable semantics.

An ARIA buffet

  • Roles: navigation, menubar, spinbutton, tablist, ...
  • States: expanded, pressed, invalid, grabbed, ...
  • Relationships: labelled-by, described-by, controls, ...
  • Properties: haspopup, dropeffect, readonly, ...

The Golden Rules of ARIA

Do NOT use an aria-* role or property,
if an equiavlent one already exists in HTML.

Host-language semantics are preferred.

Use sparingly!

Other Uses of ARIA

  • Use in other languages: SVG, MathML, ...
  • Sometimes safer to over-ride a HTML native role.
    Avoids breaking CSS or JS which deals with elements by tag names.
  • Complex relationships: text inputs in a table can be labelled by both Column and Row headers.
  • ASCII art!
    <pre role="image" aria-label="ASCII art picture of a fish">
    equivalent to <img alt="ASCII art fish"/>

Improving the screen-reader experience

Aim to provide a screenreader user with an idea of
what the button can be used for.

  1. Describe the button:
    • Announce the name of the button
    • Indicate it is a button
  2. Describe the behaviour of the button:
    • Expanded
    • Collapsed

Announce the name of the button

We were hiding the text label with CSS display:none (which prevents screenreaders from finding it) so we'll remove this.

-#main-menu-button span {
-  display: none;

The visuallyhidden class (from HTML5 Boilerplate) uses clip and overflow properties to hide the text in a 1px square box.


Now a screenreader can announce the button name.

Indicate it is a button

Adding role=button to the <div> overrides the default HTML semantic, so screenreaders can announce it as a "button" (or sometimes "clickable").

Describe the behaviour of the button

We'll use two ARIA attributes in tandem. Note that both of these attributes need to go on the button element itself.

  • aria-controls indicates a relationship between
    two elements (the button controls the list of links).
  • aria-expanded indicates whether the list is
    currently shown.

aria-controls property

aria-controls needs an ID to refer to.

  .attr('aria-controls', 'main-menu-nav')
  .keypress( function(event) {
    /* Handle keypress */
  .click(function(event) {
    /* The open/close behaviour acts on '#main-menu nav' */

aria-expanded state

  .attr('aria-controls', 'main-menu-nav')
  .attr('aria-expanded', false) // NEW, initialize
  .keypress( function(event) { /* Handle keypress */ })
  .click(function(event) {
    if (menuDisplayed === true) {
      $('#main-menu nav').hide();
      $(this).attr('aria-expanded', false) // NEW
      menuDisplayed = false;
    } else {
      $('#main-menu nav').show();
      $(this).attr('aria-expanded', true) // NEW
      menuDisplayed = true;

We initialize aria-expanded as false, to match the initial appearance of the menu, and update it whenever we show or hide the menu.

Notes about aria-expanded

  • aria-expanded doesn't actually hide the list of links - it just provides a way to indicate the state to a screenreader.
  • CSS display:none is doing the real work of hiding content - this comes via jQuery.hide().
  • DO NOT initialize aria-expanded in the HTML source. Always do this in the JS, in case the JS doesn't load for some reason.


"Works with a screenreader"

Git tag: milestone-works-with-mouse-keyboard-screenreader (ad3fa57)

Code improvements

We actually broke the golden rule of ARIA. Avoid using ARIA semantics to provide a role, where HTML already provides the role with an equivalent element.

In our case we used:


When we ought to have used:


More improvements

After replacing the <div role=button> with a real HTML <button>, we can remove a few things to simplify the code...

  • tabindex=0 is no longer needed - a <button> is focusable.
  • The jQuery.keypress() handler isn't needed either! The browser handles a keypress for <button>.
  • In fact, we must now remove the jQuery.keypress() handler, because the <button> will now have two keypress handlers, and the menu will be toggled twice from one keypress. Beware of introducing this bug!

Web Accessibility Reading List

TL;DR... a11yproject.com

A community-reviewed set of practical tips, with a gentle learning curve. If you only read one thing from my reading list, make it this website.

In fact, you might do better perusing their resources list ;-)

Using Personas

Dive Into Accessibility

A short open-source book which gives a great introduction to thinking about accessibility, along with techiniques.

Particularly useful for introducing personas when considering accessibility. For every technique, it has a discussion of who benefits, and how.

It's starting to look a little dated (19" is a large monitor!), and a few techniques have changed, but few books match this one as an introduction to the topic.

More about WAI-ARIA


Any Questions?

The End

Slides: fuzzbomb.github.io/js-a11y-intro-slideshow
Demo code: github.com/fuzzbomb/mobile-menu-a11y-demo