Notes on ARIA
I can’t find a friendly, readable guide to ARIA. So I sat down and took notes on the spec. This isn’t an exhaustive explanation of proper use — more a quick-and-dirty overview. I hope you find it helpful.
Roles
Values for the role
attribute that you can tack on an element to signal that the element is supposed to be something else. So if you did:
<div role="form"></div>
Assistive technology should pretend that the <div>
is a <form>
. It won’t add any functionality, but it will tell users that it’s a form.
You can add multiple space-separated role
values, like role="none presentation"
. They’re tried from left to right until the accessibility software sees one it supports.
<span role="switch checkbox"></span>
role
attribute reports the new ARIA 1.1 switch
value if supported, and falls back to checkbox
otherwise.Notice that roles replace what an element is described as. So if you had:
<ul role="navigation">
...
</ul>
You would be doing damage, because the <ul>
no longer reports itself as a list. Instead, wrap it in a container element with role=navigation
. Or use <nav>
, since it has that role by default.
As for why we want role
in the first place…
Interactive HTML elements are notoriously hard to style. If you need to script up <span>
s masquerading as a checkbox, slap on role="checkbox"
to ensure ARIA-enabled user agents can tell it’s supposed to be a checkbox. Though you may not need to; you can do some impressive stuff with a real <input type="checkbox">
. This is in fact the first rule of ARIA:
1. If you can use a real HTML element instead, do.
For stuff like role="img"
, role="button"
, or role="link"
, it’s better to use a regular ol’ <img>
, <button>
, or <a>
.
Support for ARIA can be spotty. Dragon NaturallySpeaking didn’t pay attention to ARIA at all until Version 13, and Version 12 is the most popular version.
For everything else, there’s rule two:
2. Use ARIA roles where existing HTML semantics are lacking.
This has two manifestations:
Roles like
scrollbar
,log
, ordirectory
describe things HTML can’t. A custom JavaScript scrollbar needsrole="scrollbar"
, and tables of contents should look like<nav role="directory">
.ARIA roles sometimes have deeper support than newer elements. Internet Explorer 8 doesn’t support
<section>
, but it does supportrole="region"
. Likewise, IE11 doesn’t support<main>
, but doesrole="main"
. However, don’t put redundant roles on pre-HTML5 elements —<form role="form">
won’t accomplish anything.
3. Use ARIA roles in non-HTML languages.
Like SVG. This is useful for document semantics in other markup languages, or implementing widgets in them.
So what all are the roles? There are 4 categories.
Abstract Roles
Never use these. The Abstract Roles exist only for classification.
If you see these in your code, something is wrong.
command
composite
input
landmark
range
roletype
section
sectionhead
select
structure
widget
window
Widget Roles
Widget Roles signal that a collection of <div>
s or whatever are supposed to be a text field, or checkbox, or group of radio buttons, etc.
Many of them pretend to be native HTML interactive elements:
ARIA role value |
Native HTML element |
---|---|
button |
<button> |
checkbox |
<input type="checkbox"> |
combobox |
<input type="text" list="datalist"> |
link |
<a> |
listbox |
<select> |
option |
<option> |
progressbar |
<progress> |
radio |
<input type="radio"> |
slider |
<input type="range"> |
spinbutton |
<input type="number"> with step , min , and max |
textbox |
<input type="text"> /<textarea> |
Beware: with these, you are responsible for scripting the behavior of the roled element to match the original element. This means handling focus properly, keyboard commands (Enter activates links, Space activates buttons, etc.), and all the little edge cases browsers already do. Using native elements is far easier and fail-resistant.
Other Widget Roles describe controls HTML doesn’t have, but are commonly used. We have widgets that primarily offer information:
alert
,dialog
, andalertdialog
call out a message that should be read before continuing, in varying degrees of interaction and politeness. (These will eventually be obsoleted by the<dialog>
element.)log
describes a dynamic area where information flows in and out, in a specific order. Think chatrooms, or live-updating server logs.marquee
is likelog
, but more like a stock ticker. It's described as “non-essential information that changes frequently”, and differs fromlog
in that it doesn’t care about order. It’s the<ul>
tolog
’s<ol>
. (It has nothing to do with<marquee>
.)status
is a dynamic description of, well, the current status of the application. Think "Loading…", "Unable to sync", or "Done, but with errors on page."timer
is a dynamic area that tracks how long something is taking, or counts down to a deadline.
And other roles describe the structure of typical interaction methods:
menu
,menubar
,menuitem
,menuitemcheckbox
, andmenuitemradio
describe how menu controls are laid out. These will eventually be obsoleted by<menu>
.tablist
,tab
, andtabpanel
describe interfaces like, well, browser tabs.listbox
is like<select>
, except you can put more than just plain text inside the options.radiogroup
indicates which radio buttons are part of a set. Use it on a wrapper around<input type="radio">
s with identicalname
s, if you can’t use<fieldset>
.grid
describes interactive tabular data, like spreadsheets.tree
describes interactive tree diagrams (filesystems, expandable JSON, etc.), andtreegrid
seems to be for tables with row folding.scrollbar
is for custom scrollbars, since you can’t style them in most browsers.tooltip
is for custom tooltips, because thetitle
attribute is really kind of terrible.
Using Widget Roles correctly can get complicated, as they’re all very different. Double-check with the spec.
Document Structure Roles
These are for describing elements as non-interactive HTML elements. You really, really should use the right element for the job, but in case your site uses a <div>
as an <img>
for some reason (like Instagram, except Instagram wouldn’t know ARIA if it bit it on the filters), that’s what these are for. If you want accessible HTML emails, which use <table>
like it was 1995, these are your only hope.
Specifically, these describe document semantics, like news articles, blog posts, diagrams, etc.
article
is almost exactly like<article>
, but you can’t nest them inside each other. Mostly obsolete.columnheader
,row
,rowgroup
, androwheader
describe parts of a<table>
. ARIA 1.1 also introducesrole="table"
, because accessibility software has heuristics for tables abused for layout. Sometimes it guesses wrong.definition
is for “a definition of a term or concept”. Like<dfn>
,<dt>
, or an<abbr>
's expansion.directory
is a table of contents. It can have links to parts of the page, or static text.group
says elements inside are related. It’s mostly used like<fieldset>
. Be sure to include a label!heading
is like<h1>
,<h2>
,<h3>
,<h4>
,<h5>
, and<h6>
. Which one is determined by thearia-level
attribute, described later.img
=<img>
. Used for<div>
s with abackground-image
(a common method before<picture>
andsrcset
), ASCII art, or other elements that should be interpreted like an image. Say, inline<svg>
.list
andlistitem
work exactly how you'd think. Use<ol>
and<ul>
instead.math
imitates the<math>
element. It can contain images of the equation for browsers that don’t support MathML, but a text description of the contents is required, often witharia-label
.note
is “a section whose content is parenthetic or ancillary to the main content of the resource.” Like<aside>
or<small>
, but more specific. (Accessibility software does not announce<small>
as a side comment, from years of abuse.)presentation
is very special. It wipes out the semantics of an element. So if you’re forced to use a<table>
for layout, mitigate the accessibility damage withrole="presentation"
.region
is almost exactly<section>
. It’s also described to be like<frame>
, but let’s not worry about that one.separator
is almost exactly<hr>
, which was a bit of a jury-rigged<section>
in HTML 4.toolbar
is, well, a toolbar. I’m not sure why this isn't a widget role. I think the the toolbar itself isn’t interactive, but its children are.
There is one more: role="document"
is similar to <body>
, but more useful when combined with a later role, application
.
Landmark Roles
Landmark Roles are for different parts of a web page, instead of inside the document the web page is presenting.
application
is for when assistive technology should transmit user interaction directly to the page, instead of intercepting as it usually does. If you use this, you must include accessible ways of navigating and controlling the application, as the assistive technology will play a minimal role insideapplication.
If your app contains documents, like a webmail client, you can restore normal assistive navigation withrole="document"
.banner
is for sitewide<header>
s. Site name, tagline, logos, top navigation, etc. Do not use more than once inside eachrole="document"
orrole="application"
. You don’t really need this;<header>
not inside<article>
automatically has this role.complementary
is like an<aside>
that isn’t within sectioning content, but has something to do with the main content. This is a pretty muddy distinction; it’s not appropriate to use it for ads or twitter feeds or recent comments or what have you, unless they’re related to the main content.contentinfo
is for stuff like copyright/license information, privacy statements, etc. Only have one perrole="document"
orrole="application"
. A<footer>
that’s not within an<article>
automatically has this role.form
=<form>
. Always use the real element unless there’s a really good reason, and I can’t think of what reason that might be.main
is where the<main>
element came from, so they’re identical. Use<main role="main">
because Internet Explorer 11- don’t support the element.navigation
is<nav>
. Just use<nav>
.search
is for forms that, well, search. Used like<form role="search">
.
And that was the roles
Which are only half of ARIA. The rest consists of attributes prefixed with aria-
, officially called “states and properties.”
aria-
attributes (states and properties)
aria-
attributes describe the state an element is in or a property it has. States are like: checked, inactive, grabbed, or busy. A property is like if a widget has autocomplete, or is described by another element.
It’s possible to use these in a static HTML page, but most likely you’ll use them with JavaScript.
Like roles, these are divided into 4 categories: Widget Attributes, Live Region Attributes, Drag-and-Drop Attributes, and Relationship Attributes.
Widget Attributes
Widget attributes describe the states and properties of, er, widgets. They can be used for native HTML interactive elements or custom ones with Widget Roles.
Some of them seem like boolean attributes (such as required
or hidden
, that don’t need values), but they’re not. Make sure to put true
or false
in the attribute values.
The states:
aria-checked
,aria-disabled
,aria-invalid
, are like CSS’s:checked
,:disabled
, and:invalid
. Applying these attributes does not qualify elements for the pseudo-classes, so that’s another reason to use the real McCoy.aria-pressed
is likearia-checked
, but for toggle buttons and switches.aria-expanded
indicates the element behaves like<details>
/<summary>
. When it’s expanded, usearia-expanded="true"
. When collapsed, usefalse
.aria-hidden
is for elements nobody should see or use, including abled users. So if you hide a menu by positioning it offscreen, setaria-hidden="true"
on it. This is largely obsoleted by thehidden
attribute.aria-selected
is for when you have an element, well, selected. It can be a single one, or applied to multiple elements, like managing files in Google Drive.
Properties indicate something applies or is possible with an element. They don’t change much over the application’s lifetime, but it’s not unheard-of.
aria-autocomplete
indicates the element should have some kind of autocomplete. This isn’t for cases where the browser supports autocomplete, but when you roll your own.aria-haspopup
indicates the element has a context menu, or some other control bits that appear when used. For custom right-click menus, or a button to show more actions.aria-label
is like a universalalt
attribute. If you use it likearia-label="Close
on a button that looks like<button>X</button>
, assistive technology will report the button as “Close”, instead of the letter X. It’s best to use real text labels, but this can be used to supplement lacking information, like Read More links viewed by assistive technologies’ “scan links” feature.aria-level
shows how deeply nested something is, with integers likearia-level="2"
. It’s for nestedgrid
,heading
,listitem
,row
, andtablist
roles: complex tables, subheadings, nested lists, etc.aria-multiline
describes if a text input allows single or multiple lines. It’s how you specify ifrole="textbox"
should be treated like<input type="text">
or<textarea>
.aria-multiselectable
indicates an element holds children that can be individually (false
) or multiply selected (true
). Scripting that behavior is up to you.aria-orientation
is forscrollbar
,separator
, orslider
to indicate if they’revertical
orhorizontal
(default).aria-readonly
is likereadonly
on<input>
. Not sure why you’d use it over the attribute.aria-required
is basically form elements’required
attribute. Largely obsolete nowadays.aria-sort
goes on grid/table headers if they’re sortable. Distinguish the two with thecolumnheader
androwheader
roles.aria-valuemax
,aria-valuemin
, andaria-valuenow
work like themin
,max
, andvalue
attributes in HTML5 forms. For backwards compatibility and non-HTML languages.aria-valuetext
provides a label for ranged inputs, where reporting the actual value doesn’t matter to the user. Your slider might be implemented with values from 0 to 10, but you can dynamically setaria-valuetext
to human-readable values like “Off”, “Low”, or “High”.
Live region attributes
A “live region” is W3C for “an element that’ll change often”. Chat widgets, status bars, collaboratively edited documents, and so on. These attributes signal that the element or its children will change, and assistive technology can notify users when they do. A more thorough (and more technically correct) guide here.
Start with the aria-live
attribute, which marks an element as a live region. Set it to aria-live="assertive"
or aria-live="polite"
, which differ in how hard they get the user’s attention. assertive
is likely to interrupt or notify the user ASAP; polite
notifies when the assistive technology is done with its current task, or signals with a tone when the region changes. For things that change a lot, like accelerometer data or signal strength, you can use aria-live="off"
to prevent flooding the user with updates.
aria-busy="true"
is applied to live regions when they’re, well, busy. “Fetching updates from server” or dumping a bunch of updates in at once are valid use-cases.
aria-atomic="true"
means the entire live region should be read as a whole; assistive technology shouldn’t announce only the changed parts. Set it on small live regions, because the entire region will be presented each update.
aria-relevant
specifies how exactly the live region will update. Using it is complicated, so I’ll just link to the spec’s advice.
Drag-and-drop attributes
If your web page has a drag-and-drop, there are a pair of attributes for draggable elements.
- If an element can be dragged, but currently isn’t, set
aria-draggable="false"
. - If an element is currently being dragged, set
aria-draggable="true"
. - If an element can’t be dragged, remove
aria-draggable
.
As for what you’re dragging the element onto, aria-dropeffect
specifies what happens when the user drops the draggable onto the “drop target”. Values are:
copy
means the dragged item will be duplicated in the drop target.move
means the dragged item will move where you drag it.link
means a reference/shortcut/link to the dropped item will be created where you drag it.execute
means the application will process the dropped item somehow. Optimizing images, for example.popup
means the application will bring up a menu asking what exactly to do with the dropped item, including Cancel.none
means nothing will happen if you drop the item there. This is the default, so you don’t have to applyaria-dropeffect
to the elements on your page that aren’t drop targets.
You can combine these values in a space-delimited list (like aria-dropeffect="popup execute
) to indicate a drop target supports multiple of these. Don’t include none
with other values; you can’t do both nothing and something.
Relationship attributes
These attributes indicate relationships between elements. Either relationships impossible in HTML (like something inside <img>
), relationships more complicated than parent-child, or for restoring parent-child relationships if you move an element outside its parent for some reason.
aria-owns
indicates that another element (identified by its id
in aria-owns
's value) should be considered a child of the first element. So for something like:
<div aria-owns="blah"></div>
<span id="blah"></span>
The <span>
will be considered a child of the <div>
. You can put space-separated id
s inside aria-owns
to consider them all children.
aria-flowto
is similar, but instead of considering the referenced element to be inside the first one, it indicates the referenced element should be considered “next”. Like changing the source order of the document. If you specify multiple ones, assistive technology is encouraged to offer the choice of id
to continue to.
aria-controls
is indicates which element(s) the current element controls. So if you rigged up your own control panel for videos, you would do:
<video src="movie.mp4" id="movie"></video>
<div role="toolbar" aria-controls="movie">
<button>Play</button>
<button>Pause</button>
</div>
aria-setsize
and aria-posinset
are for lists of elements that can’t all be in the DOM at once for performance reasons. If your app has 3500 entries, but you can only have 100 of them in the DOM before things get laggy, you would use aria-setsize="3500"
and aria-posinset
for when you remove earlier items. Put aria-setsize
and aria-posinset
on the items themselves, not their container.
If you need a widget to remember the spot it was in but the browser focus moves somewhere else, you can use aria-activedescendant
to indicate which element has focus. You can combine this attribute with regular browser focus (with tabindex="-1"
) as well as reinforcing your application’s focusing logic. Give the value the id
of an element inside the first element, or is faked with aria-owns
.
The final two, aria-labelledby
and aria-describedby
, point to elements that name/label or describe the current one. aria-labelledby
is for short gists or names of elements, and works like this:
<figure>
<img src="barack-obama.jpg" alt="" aria-labelledby="caption">
<figcaption id="caption">A photograph of Barack Obama.</figcaption>
</figure>
aria-describedby
is for longer, thorough descriptions. Like longdesc
, but where longdesc
points to a different URL, aria-describedby
points to an element on the page that holds the description.
And that’s that
Those are all the role
s and aria-
attributes from 10,000 feet. If you’re serious about accessibility, you’ll need the actual spec, but hopefully this helped to get the shape of things.