Make Your Widgets Sing with ARIA

WDCNZ

25 July 2013

Jason Kiss

@jkiss

Make Your Widgets Sing with ARIA

Introduction to ARIA widgets

It's in the HTML

  • Roles, states, and properties
  • Basic keyboard accessibility
 
<h3>Types of pet</h3>
<ul>
  <li><a href="/cat/">cat</a></li>
  <li><a href="/dog/">dog</a></li>
  <li><a href="/kunekune/">kunekune</a></li>
</ul>
<form>
  <fieldset>
     <legend>Do you like cats?</legend>
     <input type="radio" name="cats" id="yes" 
        value="yes" checked>
     <label for="yes">Yes</label>
     <input type="radio" name="cats" id="no" 
        value="no">
     <label for="no">No</label>
  </fieldset>
  <input type="submit" value="Submit">
</form>
						

Types of pet



Do you like cats?

And that's important

Examples of different assistive technologies, including screen readers, switches, Braille devices, wands, screen magnifiers.

First, there's the DOM

A sample Document Object Model tree representation.

Accessibility APIs

The contract model between user agents, accessibility APIs and assistive technologies.

Mac OS

  • Mac OS X Accessibility Protocol

Windows

  • MSAA
  • IAccessible2
  • UI Automation

Linux

  • ATK
  • AT-SPI

Inspecting the Accessibility APIs

Windows

  • AViewer
  • Accessibility Probe
  • Inspect Objects (MS Windows SDK)

Mac OS

  • Accessibility Inspector (Xcode)

Linux

  • Accerciser

Demo: Mac Accessibility Inspector

Types of pet



Do you like cats?
Mac Accessibility Inspector logo

Still, there's the DOM

Useful when:

  • the API does not support certain information, or
  • the information passed to the API is incomplete

Native vs. Custom Widgets

Example: Checkbox (1/3)

 
<input type="checkbox" id="dogs"...>
<label for="dogs">Dogs</label>

<div class="checkbox">Dogs</div>	

$('.checkbox').on('click', function() {
   $(this).toggleClass('checked');
});	

.checkbox {
   background: white url(../img/checkbox.png) 
      no-repeat 0 8px;
}
.checkbox.checked {
   background-image: url(../img/checkbox-checked.png);
}
					



Dogs


No keyboard access.

No role or state information.

Solution: WAI-ARIA

Accessible Rich Internet Applications (WAI-ARIA) 1.0

It's just attributes and values

  • roles use @role, e.g.
    role="navigation"
  • states and properties use @aria-*,
    e.g.
    aria-expanded="true"

It's part of the HTML5 specification.

Landmark roles

  • application
  • banner
  • complementary
  • contentinfo
  • form
  • main
  • navigation
  • search

http://www.w3.org/TR/wai-aria/

Document structure roles

  • article
  • columnheader
  • definition
  • directory
  • document
  • group
  • heading
  • img
  • list
  • listitem
  • math
  • note
  • presentation
  • region
  • row
  • rowheader
  • separator
  • toolbar

Native ARIA semantics

  • header → “banner” role, if not a child of section or article
  • nav → “navigation” role
  • main → “main” role
  • aside → “complementary” role
  • footer → “contentinfo” role, if not a child of section or article

Standalone widget roles

  • alert
  • alertdialog
  • button
  • checkbox
  • dialog
  • gridcell
  • link
  • log
  • marquee
  • menuitem
  • menuitemcheckbox
  • menuitemradio
  • option
  • progressbar
  • radio
  • scrollbar
  • slider
  • spinbutton
  • status
  • tab
  • tabpanel
  • textbox
  • timer
  • tooltip
  • treeitem

Composite widget roles

  • combobox
  • grid
  • listbox
  • menu
  • menubar
  • radiogroup
  • tablist
  • tree
  • treegrid

http://www.w3.org/TR/wai-aria/roles#widget_roles

Native ARIA semantics

  • button → “button” role
  • input type="checkbox → “checkbox” role
  • input type="radio" → “radio” role

Overriding native HTML semantics

 
<a href="#">Link</a>

<a href="#" role="button">Button</a>
					
 
<ul>
   <li role="button">list item button</li>
</ul>
						
  • list item button

States and properties

Widget attributes

  • aria-autocomplete
  • aria-checked (state)
  • aria-disabled (state)
  • aria-expanded (state)
  • aria-haspopup
  • aria-hidden (state)
  • aria-invalid (state)
  • aria-label
  • aria-level
  • aria-multiline
  • aria-multiselectable
  • aria-orientation
  • aria-pressed (state)
  • aria-readonly
  • aria-required
  • aria-selected (state)
  • aria-sort
  • aria-valuemax
  • aria-valuemin
  • aria-valuenow
  • aria-valuetext

Live region attributes

  • aria-atomic
  • aria-busy (state)
  • aria-live
  • aria-relevant

Drag-and-drop attributes

  • aria-dropeffect
  • aria-grabbed (state)

Relationship attributes

  • aria-activedescendant
  • aria-controls
  • aria-describedby
  • aria-flowto
  • aria-labelledby
  • aria-owns
  • aria-posinset
  • aria-setsize

http://www.w3.org/TR/wai-aria/states_and_properties#state_prop_taxonomy

Managing keyboard focus

  • tabindex="0"

    Adds an element to the Tab order at its position in the source order.
  • tabindex="-1"

    Removes an element from the Tab order.
    Makes non-focusable elements programmatically focusable.
  • tabindex="1+"
    (Avoid)
    Specifies an element's position following the default Tab order.

role="presentation"

Removes an element's default semantics.

 
<h3>Level 3 heading</h3>
					

Level 3 heading

 
<h3 role="presentation">Not a heading</h3>
					

Not a heading

 
<table>
   <caption>Layout table</caption>
   <tr><td>Name:</td><td>Jonas Salk</td></tr>
   <tr><td>Discovery:</td><td>Polio vaccine</td></tr>
</table>
						
Layout table
Name: Jonas Salk
Discovery: Polio vaccine
 
<table role="presentation">
   <caption>Not a table</caption>
   <tr><td>Name:</td><td>Jonas Salk</td></tr>
   <tr><td>Discovery:</td><td>Polio vaccine</td></tr>
</table>
						
Not a table
Name: Jonas Salk
Discovery: Polio vaccine

Not on focusable elements

Don't do this!

It won't work anyway.

 
<a href="#" role="presentation">Still a link!</a>
						
 
<button role="presentation">Still a button!</a>
						

Example: Checkbox (1/3) revisited

 
<div class="checkbox">Dogs</div>	

$('.checkbox').on('click', function() {
   $(this).toggleClass('checked');
});	

.checkbox {
   background: white url(../img/checkbox.png) 
      no-repeat 0 8px;
}
.checkbox.checked {
   background-image: url(../img/checkbox-checked.png);
}
					

Dogs


No keyboard access.

No role or state information.

Example: Checkbox (2/3)

Add keyboard support

  1. add tabindex="0"
 
<div class="checkbox" tabindex="0">Dogs</div>	
  1. add handler for Space key

$('.checkbox').on('click', function() {
   $(this).toggleClass('checked');
});	

$('.checkbox').keydown(function(e) {
   if (e.which == 32) {
      $(this).click();
   }
});

Dogs


Still no role or state information.

Example: Checkbox (3/3)

Add role and state

  1. add role="checkbox" and @aria-checked
 
<div class="checkbox" tabindex="0" 
   role="checkbox" aria-checked="false">Dogs</div>	
  1. add toggle for @aria-checked

$('.checkbox').on('click', function() {
   if ($(this).attr('aria-checked') == 'false') {
      $(this).attr('aria-checked', 'true');
   } else {
      $(this).attr('aria-checked', 'false');
   }
});	

$('.checkbox').keydown(function(e) {
   if (e.which == 32) {
      $(this).click();
   }
});



Role and state provided.

Example: range slider (1/3)

input type="range"


<label for="slider">
   Percentage (multiples of 10)
</label>

<input id="slider" type="range"
   min="0" max="100" step="10" value="0">
					

Supported in Opera, Safari, Chrome, IE10,
and FF23 (6 August 2013).


Otherwise falls back to input type="text".




Example: range slider (2/3)

Custom slider with JavaScript


<div>Percentage (multiples of 10)</div>

<div class="slider-widget">
   <div id="slider" tabindex="0"></div>
</div>
					

Keyboard accessible with tabindex="0"
and handlers for left arrow and right arrow.


No programmatic label.


No role or state information.


Percentage (multiples of 10)


Example: range slider (3/3)

Custom slider with JavaScript plus ARIA

  1. add role="slider"
  2. associate the label using @aria-labelledby
  3. add required ARIA attributes
  4. dynamically update @aria-valuenow
    and @aria-valuetext

<div id="slider-label">
   Percentage (multiples of 10)
</div>

<div class="slider-widget">
   <div id="slider" tabindex="0" role="slider" 
      aria-labelledby="slider-label" 
      aria-valuemin="0" aria-valuemax="10" 
      aria-valuenow="0" aria-valuetext="0%"></div>
</div>
					

Label, role and state provided.


Percentage (multiples of 10)


Labels and descriptions

@aria-labelledby


<div id="label">Date</div>

<input type="text" aria-labelledby="label format">

<span id="format">DD-MM-YYYY</span>						
						
Date
DD-MM-YYYY

Labels and descriptions

@aria-label


<nav role="navigation" aria-label="Main menu">
   <ul>
      …
   </ul>
</nav>
						

<button aria-label="Close">X</button>
						

Labels and descriptions

@aria-describedby


<label for="comment">Leave a comment</label>

<textarea id="comment" aria-describedby="notice">
</textarea>

<p id="notice">Note: Your comment may be published.</p>
					

Note: Your comment may be published.

Tabs: Initial markup

   

<ul>
   <li><a href="#cat">Cat</a></li>
   <li><a href="#dog">Dog</a></li>
   <li><a href="#horse">Horse</a></li>
</ul>

<div>
   <h3 id="cat">Man with cat</h2>
   ...
</div>

<div>
   <h3 id="dog">Man with dog</h2>
   ...
</div>

<div>
   <h3 id="horse">Horse and bridal party</h2>
   ...
</div>
					

Man with cat

Topless dude standing in a Christmas tree and holding a cat.The Giving Tree

Man with dog

Beefy topless dude posing with hairy dog.Pet-A-Likes: Primped

Horse and bridal party

Horse photobombing bridal party photo.Saturday Night Special: Horsing Around

Tabs: Styling

  1. add some class names and style appropriately
  2. selected tab panel is visible
  3. inactive tab panels are hidden
   
<ul class="tablist">
   <li class="current"><a href="#cat">Cat</a></li>
   <li><a href="#dog">Dog</a></li>
   <li><a href="#horse">Horse</a></li>
</ul>
<div class="tabpanel" style="display: block;">
   <h3 id="cat">Man with cat</h2>
   ...
</div>
<div class="tabpanel" style="display: none;">
   <h3 id="dog">Man with dog</h2>
   ...
</div>
<div class="tabpanel" style="display: none;">
   <h3 id="horse">Horse and bridal party</h2>
   ...
</div>


						

No role or state information provided.

Man with cat

Topless dude standing in a Christmas tree and holding a cat.
The Giving Tree

Man with dog

Beefy topless dude posing with hairy dog.
Pet-A-Likes: Primped

Tabs: Role, state, and keyboard

  1. add ARIA attributes and tabindex
  2. script appropriate keyboard interaction
   
<ul role="tablist">
   <li role="presentation">
      <a role="tab" aria-controls="panel-cat" 
         aria-selected="true" tabindex="0" 
         href="#cat">Cat</a>
   </li>
   <li role="presentation">
      <a role="tab"  aria-controls="panel-dog" 
         aria-selected="false" tabindex="-1" 
         href="#dog">Dog</a>
   </li>
   <li role="presentation">
      <a role="tab" aria-controls="panel-horse"
         aria-selected="false" tabindex="-1" 
         href="#horse">Horse</a>
   </li>
</ul>

						
   

<div id="panel-cat" role="tabpanel"
   aria-labelledby="cat"> 
   <h3 id="cat">Man with cat</h3>
   …
</div>
<div id="panel-dog" role="tabpanel" 
   aria-labelledby="dog">
   <h3 id="dog">Man with dog</h3>
   …
</div>
<div id="panel-horse" role="tabpanel" 
   aria-labelledby="horse">
   <h3 id="horse">
      Horse and bridal party
   </h3>
   …
</div>
						

Tabs: ARIA accessible

Man with cat

Topless dude standing in a Christmas tree and holding a cat.
The Giving Tree

Man with dog

Beefy topless dude posing with hairy dog.
Pet-A-Likes: Primped

Live regions

  • aria-live="assertive/polite/off"
  • aria-atomic="true/false"
  • aria-relevant="additions/removals/text/all"

*Click "Users online" heading below to start/stop demo.


<h3>Users online</h3>

<ul aria-live="polite" aria-relevant="additions removals">
</ul>
					

Users online

    Types of live regions

    role="alert"

    Default is aria-live="assertive".

    Make someone happy with this stunning 87 piece bracelet.
    All yours for just $24.99.

    A very kitsch bracelet with colourful plastic bits.

    Things I didn't cover

    • role="application"
    • role="menu"
    • role="menubar"
    • role="tree"
    • role="grid"
    • role="treegrid"
    • role="alertdialog"
    • role="spinbutton"
    • role="progressbar"
    • aria-hidden
    • aria-required
    • aria-expanded
    • aria-activedescendant
    • aria-haspopup
    • aria-dropeffect
    • aria-invalid
    • aria-pressed
    • aria-level

    ...and more

    Good news

    Happy green frog.

    Browser and screen reader support is improving all the time.

    Good support for structural roles and many common widgets and attributes.

    Not so good news

    Lego figurine with sad face.

    Still lots of variability in browser and screen reader support,
    especially for complex widgets.

    Due diligence

    Black and white photo of small white dog digging with caption 'Dig Dig Dig'.

    Do the research, as you do…

    Rules of engagement

    Accessible Rich Internet Applications (WAI-ARIA) 1.0 - W3C Candidate Recommendation 18 January 2011

    http://www.w3.org/TR/wai-aria/

    WAI-ARIA 1.0 Authoring Practices - An author's guide to understanding and implementing Accessible Rich Internet Applications

    http://www.w3.org/TR/wai-aria-practices/

    Rules of engagement

    W3C Using WAI-ARIA in HTML - W3C Working Draft 14 February 2013

    http://www.w3.org/TR/aria-in-html/

    Using WAI-ARIA in HTML

    A topless and bloody Brad Pitt smoking a cigarette, surrounded by other sweaty guys in a scene from Fight Club.

    First rule of ARIA use

    Use native HTML elements and attributes as much as possible.

    Second rule of ARIA use

    Don't override native HTML semantics, unless absolutely necessary.

    Thanks

    Image credits