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

"POUR"

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.

"Compliance"

  • 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;
        }
      });
    }); 

DEMO 1

"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

DEMO

Myth-busting - "Off-Canvas"

Off-canvas != Off-viewport

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

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

Bad:
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


      $('#main-menu-button')
        .keypress( function(event) {
          // Only respond to enter or spacebar keys
          if (event.which == 13 || event.which == 32) {
            // Spacebar usually triggers page scrolling.
            event.preventDefault();
            $(this).trigger('click');
          }
        })
        .click(function(event) {
          // The open/close behaviour
          // ...
        });
    

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

DEMO 2

"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.

DEMO

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.



$('#main-menu-button')
  .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

$('#main-menu-button')
  .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.

Demo

"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

Misc

Any Questions?

The End

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