Intro to WAI-ARIA

This tutorial is a basic intro to WAI-ARIA:

  • What it is
  • The Rules of ARIA
  • Roles
    • The Landmark Roles
    • The 10 most important roles to know
  • Properties and States
    • The top 5 properties & 4 states to know
  • Live Regions
  • ARIA Widgets

This tutorial uses RevealJS. It uses the Reveal Zoom plugin, which allows you to zoom in on elements with Alt+click (Ctrl+click on Linux). I also had to do some of my own hacking to allow you to zoom in with Ctrl++ and scroll on images.

Oh, and all images can be opened in another window or tab by clicking on them.

What is ARIA?

  • ARIA == Accessible Rich Internet Applications
  • A.K.A. WAI-ARIA. WAI == Web Accessibiliy Initiative
  • It's a set of attributes that go in HTML elements.
  • It sets state, properties, and/or roles to HTML elements.
  • It's intended to bridge the gap between what native HTML can do, and what users assistive technology need for an equivalent experience

What is ARIA

Think of ARIA as a loaded nail gun:

  • you should be very careful with it
  • you shouldn't use it without some kind of training
  • it's good for some jobs (re-shingling a roof / writing a custom widget); and
  • it's not good for other jobs (building a birdhouse / building a <button> with any other HTML element)

Myth

ARIA makes HTML accessible.

Fact

HTML is already accessible - when used properly. ARIA helps in areas where there is no equivalent HTML, or the HTML is not yet widely supported in browsers.

Myth

ARIA is replacing HTML.

Fact

HTML is here to stay.

Myth

Adding ARIA to your site makes it accessible.

Fact

Adding ARIA to your site - done properly - can enhance its accessibility. Done improperly can make it much worse.

Important Concepts

Roles, States, and Properties.

Roles

  • Roles tell AT (Assisstive Technologies) how to interpret the element.
  • The role does not affect the visual appearance of the element - do that with CSS.
  • Roles are set with the role attribute.

For example: <a href="abc.html" role="button">Some text</a> will still look like a link, but to users of AT, it will effectively be a button, and they will expect that the space bar will activate it.

Simply adding the role attribute won't change the functionality. You need to update that with Javascript.

Ensure that the click event is fired on keypress when Enter is pressed, and on keyup when the space bar is pressed.

Wouldn't it just be easier to use a <button>?

Properties

  • tells AT something about the element it's on, or defines a relationship to another element
  • is usually static once applied
  • is an attribute that starts with aria-*.

For example: <input ... aria-describedby="xyzElement">.

States

  • Gives information about a widget or element in time.
  • The value is likely to change over time
  • Is an attribute that starts with aria-*.

For example: <p aria-hidden="true">This text is not available to the accessibility tree.</p>

The Five Rules of ARIA

From Using ARIA (W3C):

  1. If you can use a native HTML element or attribute with the semantics and behavior 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.
    Also often stated:
    The First Rule of ARIA is Don't Use ARIA
  2. Do not change native semantics, unless you really have to.
  3. All interactive ARIA controls must be usable with the keyboard.
  4. Do not use role="presentation" or aria-hidden="true" on a focusable element.
  5. All interactive elements must have an accessible name.

The 10 Most Important Roles To Know

Not including landmark roles.

role="none" / role="presentation"

removes semantic meaning from the element, and all child elements depending on this element. (Ex: adding role="none" to a <table> also removes the roles of its <tr> and <td> elements, but not any <button> descendant elements.)

role="application"

  • A structure containing one or more focusable elements requiring user input, such as keyboard or gesture events, that do not follow a standard interaction pattern supported by a widget role.
  • Tells AT to pass all keystrokes through
  • Javascript will be needed to handle keystrokes
  • Should almost never be used. Maybe on a game or a complex app or widget.

role="document"

  • Placed inside a widget or role="application" where keystrokes are passed to the browser to be handled by Javascript.
  • Tells a screen reader to go back to Browse Mode and start handling keystrokes.
  • Should have a tabindex="0" to accept focus.

role="heading"

  • Tells AT the element is a <H#> heading.
  • Must be accompanied by aria-level="x" where 1 <= x <= 8.
  • Example: <div role="heading" aria-level="2"> is equivalent to <h2>.

role="group"

  • Equivalent to <fieldset>.
  • Should be used with aria-label or aria-labelledby for the <legend>

Example:

<div role="group" aria-labelledby="groupLbl">
	<h2 id="groupLbl">Contact Info</h2>

	<label for="name">Name:</label>
	<input id="name">
	...
</div>

role="list"

  • It's a list
  • If an HTML list has list-style: none;, VoiceOver will not treat it as a list, unless role="list" is in the list element.

role="link"

  • Tells AT it's a link.
  • Clicking it should go somewhere
  • Needs to be tabbable

role="img"

  • Tells AT it's an image
  • Alternate text can be provided with aria-label, aria-labelledby, or other accessible name ways

role="button"

  • Don't forget to check for the Spacebar to activate
  • Event fires on keypress with the Enter key, and on keyup for the spacebar

role="switch"

  • Like a checkbox, but different
  • A checkbox is either checked or unchecked
  • A switch is either on or off

The 5 ARIA Properties You Need To Know

aria-label="accessible name here"

  • Gives the element an accessible name
  • Should not be used on elements without a role (ex: <div> or <span>)

In the following code example, the link text (ie: it's Accessible Name) is "The Nordburg Website".

Nordburg Homepage

aria-labelledby="a space-delimited list of IDs of HTML elements on the page"

  • Uses the text of another element to give this element an accessible name
  • Should not be used on an element without a role

In the following code example, the Accessible Name for the input is "Contact Information First Name".

<h2 id="contactInfo">Contact Information</h2>

<h3 id="fName">First Name</h3>

<input id="fname" aria-labelledby="contactInfo fName">

Notes about aria-label and aria-labelledby

  • ARIA takes precedence over HTML
  • An element's Accessible Name is how assistive technology identifies an element. It's what a screen reader reads out for that element.
  • Therefore if an HTML element has a native way of defining its Accessible Name, and it has an aria-label or aria-labelledby, then the ARIA-defined accessible name takes precedence.
  • aria-labelledby takes precedence over aria-label

Examples of Accessible Name Precedence

In the following code example, the Accessible Name for the input is "First Name".

<h3 id="yourName">Your Name</h3>

<label for="fname">First Name</label>
<input id="fname">

Examples of Accessible Name Precedence

In the following code example, the Accessible Name for the input is "Given Name".

<h3 id="yourName">Your Name</h3>

<label for="fname">First Name</label>
<input id="fname" aria-label="Given Name">

Examples of Accessible Name Precedence

In the following code example, the Accessible Name for the input is "Your Name".

<h3 id="yourName">Your Name</h3>

<label for="fname">First Name</label>
<input id="fname" aria-label="Given name" aria-labelledby="yourName">

aria-describedby="a space-delimited list of IDs of HTML elements on the page"

  • Uses the text of another element to give this element an accessible description
  • Ensure the element also has an accessible name.
  • Should not be used on an element without a role.

aria-level="x"

  • Where "x" is an integer between 0 and 8 inclusive.
  • Some AT won't acknowledge a level higher than 6. So don't go higher than 6.
  • Must be used with role="heading".
  • Can be used on role="row" in a role="treegrid".

aria-haspopup="false|true|menu|listbox|tree|grid|dialog"

  • Used when a control (usually a button of some sort) reveals hidden content when activated
  • aria-haspopup="true" == aria-haspopup="menu"

The 4 ARIA States You Need To Know

aria-expanded="true|false"

  • To be used with a control (usually a button of some sort) that expands/collapses something.
  • The aria-expanded attribute needs to be on the element that controls the expansion/collapsion, not the element that expands/collapses.

aria-hidden="true|false"

  • Removes the element from the accessibility tree
  • But it's still visible.
  • Ex: on an <svg> when either the alternate text is irrelevant (ie: a decorative image) or the alternate text is already given elsewhere.

aria-current="false|true|page|step|location|date|time"

  • used when an element within a set of related elements is visually styled to indicate it is the current item in the set.
  • "false" indicates the thing is not the current item in a set.
  • "true" indicates the thing is the current item in a set.
  • "page" used in a list of links tells the Screen Reader that the current page is the one you're on. (ex: "<a href="thisPage.html" aria-current="page">.)
  • "step" when a list of steps in a process is shown, and you're on a current step.
  • "location" used in an environment or context, like in a flow chart
  • "date" used in a collection of dates
  • "time" used in a collection of times

aria-current="step" Example

From the Canada Job Bank user registration process:

A 4 part process with each having a circle and text indicating the number and name of the step. An annotation showing where aria-current=step would go.  Click to enlarge.

aria-current="step" Example

From the Canada Job Bank user registration process:

The HTML code showing the aria-current=step in a multi-step process.  Click to enlarge.

aria-pressed="true|false|mixed"

  • Turns a button into a toggle
  • "mixed" is for when the toggle changes the value of more than one thing which may have different values

Landmark Roles

HTML Role equivalent
<header> role="banner"
<main> role="main"
<footer> role="contentinfo"
<nav> role="navigation"
<aside> role="complementary"
<section> role="region"
<form> role="form"
(none) role="search"

Live Regions

  • Provides screen readers with changed content without stealing focus, or changing context.
  • Messages should be short
  • Can be defined with either the role, or aria-live attribute
  • Remember that using role overrides the inherent role of its element.
    Ex: <h2 role="status"> is no longer a level 2 heading. It's a status message.
  • Rule-of-thumb: Screen readers read when the contents of the live region when the contents change; not when they're simply unhidden.
  • Most of the time aria-live should be used instead of a live role.

aria-live values

  • aria-live can be "polite", "assertive", or "off"
  • "polite" tells a scren reader "finish what you're saying. Then read this out."
  • "assertive" tells a screen reader "this is time sensitive. Drop what you're saying, and read this out right now."
  • "off" tells a screen reader to not read out any changes.
  • The vast majority of time, use "polite".

Live Roles

  • To give an element a live role: role="status|alert|log|progressbar|marquee|timer"
  • the role attribute has to be on the element before the text content is changed.
  • The current support for role="progressbar|marquee|timer"
  • role="alert|status|log" is mostly supported (Accessibility Support)

Live Region Attributes To Know

  • aria-atomic="true|false" tells a screen reader to either just read out any changes, or read the entire live region when a change occurs. (The default is false)
  • aria-relevant="additions|removals|text|all". (The default is text.)
  • If it will take time between when changes to a live region starts to change and when it finishes changing, add aria-busy="true" so as the live region is changing, then set it to aria-busy="false" so screen readers won't read out changes until all changes are made.

How To Implement

On your site template, have a <div> at the bottom of the DOM that's hidden from screen readers and use Javascript to update content whenever you need to.

Example:

HTML


		...
		</footer>
		<div class="sr-only" aria-live="polite" id="msgCentre"></div>
	</body>
</html>
						

How To Implement

Javascript


function liveMsg ($msg) {
	let $msgCentre = getElementById("msgCentre");
	$msgCentre.textContent = $msg;
} // End of liveMsg
						

ARIA Widgets

  • ARIA widgets are interactive components that are usually more complex than what HTML offers.
  • Usually mimicing an interface common on desktop apps
  • They're built using existing HTML with certain ARIA attributes, with JavaScript to provide functionality.
  • There are many widgets, and some details can be found at the ARIA Authoring Practices Guide (APG) and the ARIA 1.2 spec
  • Note that the ARIA 1.2 spec contains normative information (ie: required for conformance) and the APG contains examples how how the widgets may be implemented in some contexts (ie: non-normative, or informational).

Top 5 ARIA Widgets To Know

There are lots, but here are some common ones for which HTML counterparts don't exist:

  • Tabbed interface
  • Menus
  • Grid
  • Tree
  • Dialog

Tabbed Interface

  • For when a page has sections of content, only one of which should be shown at a time.
  • The sections of content are designated with role="tabpanel" and they're selected with interactive elements designated with role="tab" which are listed in something designated with role="tablist".
  • On mobile devices, there often isn't enough horizontal room to show all tabs, so often on mobile devices, the tabbed interface is replaced with an accordian widget (a series of disclosure widgets where only one is open at a time).

Tabbed Interface Keystrokes

  • The Left and Right arrow keys change selection, and optionally exposes that tabpanel.
  • When focus is before the tablist, Tab takes you to the tab that's currently selected. When a tab is selected Tab takes you to the first focusable element after the tablist, possibly the tabpanel itself.

Tabbed Interface

A simple tabbed interface showing the tabs, tablist, and a tabpanel.  Click to enlarge.

Menus

  • Used for actions to be performed within a page. Not for navigation.
  • From W3C WAI-ARIA 1.2: A menu is often a list of common actions or functions that the user can invoke. The menu role is appropriate when a list of menu items is presented in a manner similar to a menu on a desktop application.
  • Example: From Google Spreadsheets:
    A simple menu showing the menu, menubar, and menuitems.  Click to enlarge.

Grid

  • From W3C WAI-ARIA 1.2: a collection of one or more rows with one or more cells where some or all cells in the grid are focusable by using methods of two-dimensional navigation, such as directional arrow keys.
  • ie: the grid is traversable with the keyboard, but cells don't need to be interactive.
  • Could be like a <table>, but doesn't have to be. It is based on the concept of a <table>
  • Elements with role="grid" have descendant elements with role="row" or role="rowgroup".
  • Elements with role="gridcell|rowheader|columnheader" must have an ancestor element with role="row".

Grid

A grid with gridrows, gridcells and my keyboard focus indicator.  Click to enlarge.

Grid

The HTML code of an HTML table with role=grid and td's have tabindex=-1 to receive focus programmatically, but the interactive element with my focus has tabindex=0.  Click to enlarge.
  • Note the lack of role attributes in the <tr> and <td> elements. That's because they have the correct implicit roles.

Tree

  • From W3C WAI-ARIA 1.2: A widget that allows the user to select one or more items from a hierarchically organized collection.
  • Like a file system's directory structure, or the tag tree in a PDF authoring tool like Adobe Acrobat or Foxit Pro
  • Traversable with a keyboard
  • A tree has nodes with role="treeitem". The tree "root" has role="tree".
  • A node can either have children (in which case it's called a "Parent Node"), or not (in which case it's called an "End Node".)
  • If a parent node's children are visible, it's said to be an "Open Node" (with aria-expanded="true"). Else, it's a "Closed Node" (with aria-expanded="false").

Tree

A tree with a root node, parent nodes, child nodes, open and closed nodes, end nodes, and my keyboard focus indicator.  Click to enlarge.

Tree

The HTML code of an unordered list with role=tree and li's have role=none, whose a elements have role=treeitem and tabindex=-1 to receive focus programmatically, but the interactive element with my focus has tabindex=0.  Click to enlarge.
  • Note the role="group"s on the inner <ul>s rather than an inner role="tree".

Dialog

  • From W3C WAI-ARIA 1.2: A dialog is a descendant window of the primary window of a web application. For HTML pages, the primary application window is the entire web document, i.e., the body element.
  • In 2023, the native <dialog> element has gained wide support. But you may still see ARIA diaglog widgets around.
  • A dialog box must have an accessible name, usually defined by either aria-label or aria-labelledby.
  • If the element that has role="dialog" also has aria-modal="true", that tells AT that everything else should be ignored. This is very bad if the rest is still in the focus order.
  • aria-modal="true" replaces the old method of setting the non-dialog parts of the page to aria-hidden="true". In those pages, the dialog shouldn't be a descendant of any element with aria-hidden="true"

Dialog

A dialog box with a semi-transparent background over the non-dialog box.  Click to enlarge.

Dialog

The HTML code of a button that opens the dialog, and the div with role=dialog aria-modal=true and aria-labelledby pointing to the h2 in the dialog box. Click to enlarge.

WAI-ARIA Widgets

A screenshot of the ARIA Authoring Practices Patterns page showing a links to a few patterns such as Alert, Buttons, Breadcrumbs.

In Conclusion

Questions

Homework

  • Fork the ARIA Intro Tutorial Repo at GitHub.
  • Each directory has an HTML file to be viewed in a browser. Insructions are near the top of each page.
  • Set up GitHub Pages for your fork.
  • When you've completed the assignments, publish your fork to your GitHub Pages and give the URL to your facilitator to check it over.