WAI-ARIA

An introduction to Accessible Rich Internet Applications

WAI-ARIA

An introduction to Accessible Rich Internet Applications

Patrick H. Lauke / Version 1.0.29012015

what is ARIA
and why do we need it?

the problem

it's "easy" (in most cases) to make static web content accessible, but nowadays the web strives to be an application platform

complex web applications require structures (e.g. interactive controls) that go beyond what regular HTML offers (though some of this introduced with HTML5 ... more on that later)

jQuery UI

Polymer Project

the problem

when building interactive controls, some (too many) web developers go "all out" on the JavaScript/CSS, but ignore/sidestep regular HTML controls completely

<div onclick="...">Test</div>

faked button

<div tabindex="0"
onclick="...">Test</div>

faked button with focus

<div tabindex="0"
onkeyup="..."
onclick="...">Test</div>

faked button with focus and keyboard handling

for a sighted mouse/keyboard user
this is a button...

...but what about a screenreader user?

compare <div> to a real <button>

faked button versus real <button>

"test"
versus
"test button – to activate press SPACE bar"

more complex examples...

jQuery UI - Slider

What's the experience here for assistive technology users?

the problem

generic/inappropriate HTML elements, with extra JavaScript/CSS on top...but they're still recognised and exposed as <span>, <div>, etc

Diagram: when focused on an element, such as a button, the browser exposes information (name, value, role, state, description) to the OS accessibility API

Operating systems and other platforms provide interfaces that expose information about objects and events to assistive technologies
Java Accessibility API [JAPI], Microsoft Active Accessibility [MSAA], the Mac OS X Accessibility Protocol [AXAPI], the Gnome Accessibility Toolkit (ATK) [ATK], and IAccessible2 [IA2]

Marco Zehe: Why accessibility APIs matter

assistive technologies

assistive technologies

Léonie Watson - Basic screen reader commands for accessibility testing

inspection tools

inspection tools

test using assistive technologies (e.g. screenreaders), however...

assistive technologies often use heuristics to repair incomplete/broken accessibility API information - so we want to check what's actually exposed to the OS/platform.

of course, browsers also have bugs/incomplete support...

Firefox DOM inspector extension, showing the accessibility tree and accessibility properties - exposed by Firefox to the platform/OS accessibility API - of an element in a page

Firefox DOM Inspector

chrome://accessibility page, showing all extensions and open tabs, with the option to display the raw accessibility tree for each

chrome://accessibility view of the raw accessibility tree

aViewer window showing MSAA, ARIA, HTML and UIAutomation information of a focused element in Firefox

Accessibility Viewer (aViewer)

Safari's Web Inspector, showing a separate 'Accessibility' section under its 'Node' inspection sidebar, listing basic information such as the current node's computed role

James Craig - Using ARIA 1.0 and the WebKit Accessibility Node Inspector

Xcode's Accessibility Inspector, showing accessibility tree/attributes for a selected element in Chrome

Xcode Accessibility Inspector
(but for Chrome, remember to turn on accessibility mode in chrome://accessibility)

compare <div> to a real <button>

faked button versus real <button>

aViewer showing that the faked button is exposed as a simple 'div'
aViewer showing that a real button element is exposed with a role of 'button', with additional default values being exposed such as MSAA accName

if you use custom (not standard HTML) widgets,
use ARIA to ensure correct info is exposed

what is ARIA?

W3C - Accessible Rich Internet Applications (WAI-ARIA) 1.0

ARIA defines HTML attributes to convey correct role, state, properties and value

Diagram: ARIA attributes: 'role' and 'aria-*'
Diagram: ARIA abstract roles: widget, document structure, landmark

W3C - WAI-ARIA 1.0 - 5.3.1 The Roles Model

Diagram: ARIA widgets: simple and composite
Diagram: ARIA attributes: widget ('aria-checked'), live region ('aria-live'), drag & drop ('aria-dragged'), relationship ('aria-labelledby')

W3C - WAI-ARIA 1.0 - 6. Supported States and Properties

what information does ARIA provide?

this information is mapped by the browser to the operating system's accessibility API and exposed to assistive technologies.

extra benefit: AT will (may) automatically announce widget-specific hints and prompts (e.g. JAWS "... button - to activate, press SPACE bar")

<div tabindex="0"
role="button" onkeyup="..."
onclick="...">Test</div>

faked button with appropriate role

aViewer showing that a faked button using a 'div', but with correct role=button, is exposed the same way as a native HTML 'button' element element

use ARIA to:

ARIA roles and attributes: restrictions

what ARIA doesn't do...

ARIA is not magic: it only changes how assistive technology interprets content.

all of this is still your responsibility...

ARIA support: browsers

ARIA support: assistive technologies

ARIA support: libraries

"support" does not mean that everything works, though...

In principle ARIA works in all markup languages...but obviously that depends on user agent/AT support. We'll focus on ARIA and HTML

Sidenote: Using ARIA to enhance SVG accessibility

using WAI-ARIA in HTML

W3C - Using WAI-ARIA in HTML

the 5 rules of ARIA use

1. don't use ARIA

Fight Club spoof picture, with Steve Faulkner: The first rule of ARIA club is...you don't use ARIA

If you can use a native HTML element [HTML5] or attribute with the semantics and behaviour you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.

2. don't change native semantics

unless you really have to / know what you're doing

don't do this:

<h1 role="button">heading button</h1>

do this instead:

<h1><span role="button">heading button</span></h1>

otherwise the heading is no longer a heading
(e.g. AT users can't navigate to it quickly)

don't do this:

<ul role="navigation">
    <li><a href="...">...</a></li>
    ...
</ul>

do this instead:

<div role="navigation">
	<ul>
        <li><a href="...">...</a></li>
        ...
    </ul>
</div>

otherwise the list is no longer a list
(e.g. AT won't announce "list with X items...")

3. make interactive ARIA controls keyboard accessible

All interactive widgets must be focusable and scripted to respond to standard key strokes or key stroke combinations where applicable. [...]

Refer to the keyboard and structural navigation and design patterns sections of the WAI-ARIA 1.0 Authoring Practices

4. don't "neutralise" focusable elements

don't use role="presentation" or aria-hidden="true" on a visible focusable element. Otherwise, users will navigate/focus onto "nothing".


<!-- don't do this... -->

<button role="presentation">press me</button>

<button aria-hidden="true">press me</button>

<span tabindex="0" aria-hidden="true">...</span>

5. interactive elements must have an accessible name


<!-- don't do this... -->

<span tabindex="0" role="button">
    <span class="glyphicon glyphicon-remove-sign"></span>
</span>

<span tabindex="0" role="button" aria-label="Delete">
    <span class="glyphicon glyphicon-remove-sign"></span>
</span>

<span tabindex="0" role="button" title="Delete">
    <span class="glyphicon glyphicon-remove-sign"></span>
</span>

<span tabindex="0" role="button">
    <span class="glyphicon glyphicon-remove-sign">
      <span class="...">Delete</span>
    </span>
</span>

refer to WAI-ARIA 1.0 - 5.2.7. Accessible Name Calculation

WAI-ARIA spec can be dry/technical - use for reference
W3C - WAI-ARIA 1.0 Authoring Practices more digestible.

ARIA and HTML5

ARIA is used when building/denoting things that native HTML can't do

HTML5 introduces new elements, element types, attributes that solve some of these situations

Using aViewer we see that native HTML5 input with type='range' is exposed correctly as a 'slider'

example: HTML5 slider

HTML5 accessibility

Implementation status for HTML5 element/attribute accessibility mappings

HTML5 now also includes WAI-ARIA ...

... meaning we can validate pages with (static) ARIA validator.w3.org

common structures and widgets

(not an exhaustive list - enough to understand concepts)

using ARIA to provide structure

heading


<p class="heading1">Heading 1</p>
...
<p class="heading2">Heading 2</p>
...
<p class="heading3">Heading 3</p>
...

<p class="heading1" role="heading" aria-level="1">Heading 1</p>
...
<p class="heading2" role="heading" aria-level="2">Heading 2</p>
...
<p class="heading3" role="heading" aria-level="3">Heading 3</p>
...

example: headings

list


<div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    ...
</div>

generally more complex (big markup structures that boil down to essentially "a list of things...")


<div role="list">
    <div role="listitem">...</div>
    <div role="listitem">...</div>
    <div role="listitem">...</div>
    ...
</div>

example: list/listitem

landmarks

Classic blog structure using div elements with id attributes 'header', 'sidebar', 'content', 'footer' and individual posts marked up with div elements given class name 'post'

adapted from HTML5 Doctor - Designing a blog with html5

Classic blog structure using div elements, now complemented with ARIA role attributes 'banner', 'navigation', 'main', 'contentinfo' and individual posts with role of 'article'

why define landmarks?

Illustration of how the blog structure with ARIA role attributes is listed in NVDA's Elements List > Landmarks

doesn't HTML5 solve this?

Classic blog structure, recoded using HTML5 structural elements: header, nav, main, article, footer

adapted from HTML5 Doctor - Designing a blog with html5

Illustration of how the blog structure with HTML5 structural elements is listed in NVDA's Elements List > Landmarks

using ARIA for
simple/standalone widgets

button


<span tabindex="0" class="...">Button?</span>

example: button


<span tabindex="0" role="button" class="...">Button!</span>

toggle button


<span tabindex="0" class="...">Option</span>

<span tabindex="0" class="... pressed">Option</span>

example: [TODO] span and fixed with ARIA


<span tabindex="0" role="button"
      aria-pressed="false" class="...">Option</span>

<span tabindex="0" role="button"
     aria-pressed="true" class="... pressed">Option</span>

checkbox


<span tabindex="0" class="...">Option</span>

<span tabindex="0" class="... checked">Option</span>

example: checkbox


<span tabindex="0" role="checkbox"
      aria-checked="false" class="...">Option</span>

<span tabindex="0" role="checkbox"
     aria-checked="true" class="... checked">Option</span>

similar to toggle button, but announced differently by AT


aria-checked="true"

aria-checked="false"

aria-checked="mixed" <!-- "partially" checked -->

example: tri-state checkbox

radio button


<span tabindex="0" class="...">Yes</span>
<span tabindex="0" class="... selected">No</span>
<span tabindex="0" class="...">Maybe</span>

example: radio button


<span tabindex="-1" role="radio"
      aria-checked="false" class="...">Yes</span>
<span tabindex="0" role="radio"
      aria-checked="true" class="... selected">No</span>
<span tabindex="-1" role="radio"
      aria-checked="false" class="...">Maybe</span>

link


<span tabindex="0"
      onclick="document.location='...'">link</span>

<span tabindex="0" role="link"
      onclick="document.location='...'">link</span>

tooltip


<span tabindex="0" onmouseover="..." onfocus="...">...</span>
...
<span>this is the tooltip text</span>

<span tabindex="0" onmouseover="..." onfocus="..."
      aria-describedby="tooltip">...</span>
...
<span role="tooltip" id="tooltip">
      this is the tooltip text</span>

example: tooltip

status bar


<span role="status">
      some form of status bar message...</span>

example: status bar

alert message


<span role="alert">
      an alert message (no user interaction)</span>

example: alert

progress bar


<div>
    <span style="width:40%"></span>
</div>

<div tabindex="0" role="progressbar" aria-label="..."
     aria-valuemin="0" aria-valuemax="100"
     aria-valuenow="40" aria-valuetext="40% complete">
    <span style="width:40%"></span>
</div>

example: progress bar

slider

<!-- taken from jQueryUI -->

<div ... class="ui-slider ...">
    <span class="ui-slider-handle ..."
          tabindex="0" style="left: 7%;"></span>
</div>

example: standard jQueryUI slider


<div ... class="ui-slider ..." role="slider"
     aria-valuemin="0" aria-valuemax="100"
     aria-valuenow="40" aria-valuetext="40%">
    <span class="ui-slider-handle ..."
          tabindex="0" style="left: 40%;"></span>
</div>

Hans Hillen's Accessible jQuery-ui Components Demonstration

dialog


<div role="dialog" tabindex="0" aria-labelledby="dialog-header">
  <div id="dialog-header">My custom dialog</div>
  ...
</div>

example: jQueryUI dialog (enhanced)

Karl Groves - a11yDialog

using ARIA for
complex/composite widgets

menu


<div role="menu"</div>
    <div role="menuitem" ...>...</div>
    <div role="menuitem" ...>...</div>
    <div role="menuitem" ...>...</div>
    ...
</div>

<div role="menubar">
  <div role="menuitem" ...> ...
    <div role="menu"</div>
      <div role="menuitem" ...>...</div>
      <div role="menuitem" ...>...</div>
      <div role="menuitem" ...>...</div>
      ...
    </div>
  </div>
  ...
</div>

example: Open Ajax Alliance - Menubar

most suitable for real "application-like" web-apps - arguably not appropriate for general "website navigation"

W3C - Web Accessibility Tutorials - Web Application Menus

Adobe - Accessible Mega Menu

W3C - Web Accessibility Tutorials - Fly-out Menus

Heydon Pickering - Practical ARIA - Simple dropdowns

tabs / accordions


<div role="tablist" ...>
  <div role="tab" aria-controls="panel1"
       aria-selected="true"...>Tab 1</div>
  <div role="tab" aria-controls="panel2" ...>Tab 2</div>
  <div role="tab" aria-controls="panel3" ...>Tab 2</div>
</div>

<div role="tabpanel" id="panel1" aria-hidden="false">...</div>
<div role="tabpanel" id="panel2" aria-hidden="true">...</div>
<div role="tabpanel" id="panel3" aria-hidden="true">...</div>

example: Open Ajax Alliance: Tab Panel

variations on this theme: Marco Zehe - Advanced ARIA tip #1: Tabs in web apps

not appropriate if you're just marking up a site navigation...


<div role="tablist" ...>
  <div role="tab" aria-controls="panel1" ...>Tab 1</div>
  <div role="tabpanel" id="panel1">...</div>
  <div role="tab" aria-controls="panel2" ...>Tab 2</div>
  <div role="tabpanel" id="panel2">...</div>
  <div role="tab" aria-controls="panel3" ...>Tab 2</div>
  <div role="tabpanel" id="panel3">...</div>
</div>

example: Hans Hillen - Accessible jQuery-ui Components: Accordion

sometimes better to keep it simple (series of expand/collapse controls): whatsock - AccDC Technical Style Guide / AccDC Technical Style Guide (GitHub)

listbox


<div role="listbox" aria-activedescendant="opt2" tabindex="0">
    <div role="option" id="opt1">Option 1</div>
    <div role="option" id="opt2" class="active">Option 2</div>
    <div role="option" id="opt3">Option 3</div>
</div>

example: James Craig - multiselect listbox

combobox

<!-- similar to <select> -->
<input type="text" role="combobox" aria-expanded="true"
     aria-autocomplete="list" aria-owns="optlist"
     aria-activedescendant="opt2">

<div role="listbox" id="optlist">
    <div role="option" id="opt1">Option 1</div>
    <div role="option" id="opt2" class="active">Option 2</div>
    <div role="option" id="opt3">Option 3</div>
</div>

example: Open Ajax Alliance: Combobox with aria-autocomplete=list

tree

<!-- list with selectable items, expand/collapse, nesting -->
<div role="tree">
  <div role="treeitem">...</div>
  <div role="treeitem">...</div>
  <div role="treeitem">...
    <div role="group">
      <div role="treeitem">...</div>
      <div role="treeitem">...</div>
    </div>
  </div>
  ...
</div>

example: Tree example (no ARIA used) / Tree example (with ARIA)

support very poor on mobile (as with many complex ARIA widgets)!

grid

<!-- interactive table/spreadsheet -->
<div role="grid">
  <div role="row">
    <div role="columnheader">...</div>
    <div role="columnheader">...</div>
  </div>
  <div role="row">
    <div role="gridcell">...</div>
    <div role="gridcell">...</div>
  </div>
  ...
</div>  

example: Open Ajax Alliance: Grid example

sometimes better to simplify: Dennis Lembree - Interactive elements within a grid layout

managing focus and keyboard interactions

the basics

to be usable – all interactive controls must be:

problem with custom controls

solution

complex widgets and focus

keyboard navigation within widgets

not all complex widgets lend themselves to "roving" tabindex - e.g. role="combobox" needs aria-activedescendant, as actual focus must remain inside the textbox.

W3C WAI-ARIA 1.0 Authoring Practices - 3.1.3. Keyboard Navigation within Widgets

<!-- roving tabindex example -->
<div role="radiogroup">
  <div role="radio" aria-checked="true" tabindex="0" ...> ...
  <div role="radio" aria-checked="false" tabindex="-1" ...> ...
  <div role="radio" aria-checked="false" tabindex="-1" ...> ...
</div>

only one radio button inside the group has focus

<!-- roving tabindex example -->
<div role="radiogroup">
  <div role="radio" aria-checked="false" tabindex="-1" ...> ...
  <div role="radio" aria-checked="true" tabindex="0" ...> ...
  <div role="radio" aria-checked="false" tabindex="-1" ...> ...
</div>

changing the selection dynamically changes tabindex, aria-checked and sets focus() on the newly selected radio button

<!-- activedescendant example -->
<div role="radiogroup" tabindex="0" aria-activedescendant="rad1" >
  <div role="radio" id="rad1" aria-checked="true" ...> ...
  <div role="radio" id="rad2" aria-checked="false" ...> ...
  <div role="radio" id="rad3" aria-checked="false" ...> ...
</div>

radiogroup itself takes focus - selected radio button only identified via aria-activedescendant

<!-- activedescendant example -->
<div role="radiogroup" tabindex="0" aria-activedescendant="rad2" >
  <div role="radio" id="rad1" aria-checked="false" ...> ...
  <div role="radio" id="rad2" aria-checked="true" ...> ...
  <div role="radio" id="rad3" aria-checked="false" ...> ...
</div>

changing the selection dynamically changes aria-activedescendant on the radiogroup, aria-checked on the radio button - but focus still remains only on the radiogroup

Medialize - ally.js

live regions

making AT aware of content changes

best way to notify users of assistive technologies of new content (a new element added to the page, made visible, a change in text) is to move focus() programmatically to it.

but this is not always possible, as it would interrupt the user's current actions...

example: faked button with notification via focus()

ARIA live regions

example: faked button with notification using aria-live and aria-atomic

some roles have implicit live region - e.g. role="alert"

unfortunately, support is...flaky

Karl Groves - jQuery Live Regions

drag & drop

Dev.Opera - Gez Lemon - Accessible Drag and Drop Using WAI-ARIA

example: Open Ajax Alliance - Drag and Drop / Gez Lemon's Drag and Drop example

support is still bad (particularly on mobile) - consider refactoring or providing alternative instead

what about role="application" ?

document vs application mode

assistive technologies/screenreaders generally operate in two modes: document mode and application mode (terminology varies)

SSB Bart Group - How Windows Screen Readers Work on the Web

role="application"
forces application mode

the result

example of a complex web application: Google Mail
Google Mail overlaid with a simple graphic, denoting the page as a whole as a candidate for role=application, while the content of an email itself would have role=document to enable reading mode

(Google Mail doesn't use role="application" ... for illustrative purposes only)

you don't need role="application"...

in both cases, assistive technologies recognise them and automatically switch to applicable mode / pass relevant keystrokes to the page

tl;dr: in most situations, you won't need role="application"

use role="application" with caution/sparingly
(generally not to entire page)

use role="document" to then denote content areas

Marco Zehe - If you use the WAI-ARIA role “application”, please do so wisely!

ARIA to explicitly define relationships

beyond what HTML offers natively

Form field with a label and additional explanatory text below

example: form enhancement using aria-describedby (plus aria-required and aria-invalid)

A grid of text entry fields with row and column headers

example: form enhancement using aria-labelledby

ARIA for remediation

if your page/app uses inappropriate markup, ARIA can be used to patch some of the issues (if it can't be fixed properly)...


<table role="presentation">
    <tr>
        <td>Layout column 1</td>
        <td>Layout column 2</td>
    </tr>
</table>

example: layout table remediation

web components, angular, etc?

Polymer's Paper Elements implementation of slider uses ARIA attributes

Polymer - Paper Elements - Slider

Addy Osmani / Alice Boxhall - Accessible Web Components

W3C Editor's Draft - Custom Elements - 11.1 Custom Tag example

AngularJS Developer Guide - Accessibility with ngAria

recap...

get in touch

@patrick_h_lauke
github.com/patrickhlauke/aria
patrickhlauke.github.io/aria/presentation/
slideshare.net/redux
paciellogroup.com
splintered.co.uk

Creative Commons: Attribution Non-Commercial Share-Alike