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.
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:
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.
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="link", it’s better to use a regular ol’
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:
role="scrollbar", and tables of contents should look like
ARIA roles sometimes have deeper support than newer elements. Internet Explorer 8 doesn’t support
<section>, but it does support
role="region". Likewise, IE11 doesn’t support
<main>, but does
role="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.
Never use these. The Abstract Roles exist only for classification.
If you see these in your code, something is wrong.
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:
||Native HTML element|
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:
alertdialogcall out a message that should be read before continuing, in varying degrees of interaction and politeness. (These will eventually be obsoleted by the
logdescribes a dynamic area where information flows in and out, in a specific order. Think chatrooms, or live-updating server logs.
log, but more like a stock ticker. It's described as “non-essential information that changes frequently”, and differs from
login that it doesn’t care about order. It’s the
<ol>. (It has nothing to do with
statusis a dynamic description of, well, the current status of the application. Think "Loading…", "Unable to sync", or "Done, but with errors on page."
timeris 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:
menuitemradiodescribe how menu controls are laid out. These will eventually be obsoleted by
tabpaneldescribe interfaces like, well, browser tabs.
<select>, except you can put more than just plain text inside the options.
radiogroupindicates which radio buttons are part of a set. Use it on a wrapper around
<input type="radio">s with identical
names, if you can’t use
griddescribes interactive tabular data, like spreadsheets.
treedescribes interactive tree diagrams (filesystems, expandable JSON, etc.), and
treegridseems to be for tables with row folding.
scrollbaris for custom scrollbars, since you can’t style them in most browsers.
tooltipis for custom tooltips, because the
titleattribute 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.
articleis almost exactly like
<article>, but you can’t nest them inside each other. Mostly obsolete.
rowheaderdescribe parts of a
<table>. ARIA 1.1 also introduces
role="table", because accessibility software has heuristics for tables abused for layout. Sometimes it guesses wrong.
definitionis for “a definition of a term or concept”. Like
<dt>, or an
directoryis a table of contents. It can have links to parts of the page, or static text.
groupsays elements inside are related. It’s mostly used like
<fieldset>. Be sure to include a label!
<h6>. Which one is determined by the
aria-levelattribute, described later.
<img>. Used for
<div>s with a
background-image(a common method before
srcset), ASCII art, or other elements that should be interpreted like an image. Say, inline
listitemwork exactly how you'd think. Use
<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 with
noteis “a section whose content is parenthetic or ancillary to the main content of the resource.” Like
<small>, but more specific. (Accessibility software does not announce
<small>as a side comment, from years of abuse.)
presentationis 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 with
regionis almost exactly
<section>. It’s also described to be like
<frame>, but let’s not worry about that one.
separatoris almost exactly
<hr>, which was a bit of a jury-rigged
<section>in HTML 4.
toolbaris, 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,
Landmark Roles are for different parts of a web page, instead of inside the document the web page is presenting.
applicationis 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 inside
application.If your app contains documents, like a webmail client, you can restore normal assistive navigation with
banneris for sitewide
<header>s. Site name, tagline, logos, top navigation, etc. Do not use more than once inside each
role="application". You don’t really need this;
<article>automatically has this role.
complementaryis 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.
contentinfois for stuff like copyright/license information, privacy statements, etc. Only have one per
<footer>that’s not within an
<article>automatically has this role.
<form>. Always use the real element unless there’s a really good reason, and I can’t think of what reason that might be.
mainis where the
<main>element came from, so they’re identical. Use
<main role="main">because Internet Explorer 11- don’t support the element.
<nav>. Just use
searchis for forms that, well, search. Used like
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.
Like roles, these are divided into 4 categories: Widget Attributes, Live Region Attributes, Drag-and-Drop Attributes, and Relationship 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
hidden, that don’t need values), but they’re not. Make sure to put
false in the attribute values.
aria-invalid, are like CSS’s
:invalid. Applying these attributes does not qualify elements for the pseudo-classes, so that’s another reason to use the real McCoy.
aria-checked, but for toggle buttons and switches.
aria-expandedindicates the element behaves like
<summary>. When it’s expanded, use
aria-expanded="true". When collapsed, use
aria-hiddenis for elements nobody should see or use, including abled users. So if you hide a menu by positioning it offscreen, set
aria-hidden="true"on it. This is largely obsoleted by the
aria-selectedis 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-autocompleteindicates 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-haspopupindicates 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-labelis like a universal
altattribute. If you use it like
aria-label="Closeon 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-levelshows how deeply nested something is, with integers like
aria-level="2". It’s for nested
tablistroles: complex tables, subheadings, nested lists, etc.
aria-multilinedescribes if a text input allows single or multiple lines. It’s how you specify if
role="textbox"should be treated like
aria-multiselectableindicates an element holds children that can be individually (
false) or multiply selected (
true). Scripting that behavior is up to you.
sliderto indicate if they’re
<input>. Not sure why you’d use it over the attribute.
aria-requiredis basically form elements’
requiredattribute. Largely obsolete nowadays.
aria-sortgoes on grid/table headers if they’re sortable. Distinguish the two with the
aria-valuenowwork like the
valueattributes in HTML5 forms. For backwards compatibility and non-HTML languages.
aria-valuetextprovides 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 set
aria-valuetextto 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="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.
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
- If an element is currently being dragged, set
- If an element can’t be dragged, remove
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:
copymeans the dragged item will be duplicated in the drop target.
movemeans the dragged item will move where you drag it.
linkmeans a reference/shortcut/link to the dropped item will be created where you drag it.
executemeans the application will process the dropped item somehow. Optimizing images, for example.
popupmeans the application will bring up a menu asking what exactly to do with the dropped item, including Cancel.
nonemeans nothing will happen if you drop the item there. This is the default, so you don’t have to apply
aria-dropeffectto 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.
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
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>
<span> will be considered a child of the
<div>. You can put space-separated
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-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-posinset for when you remove earlier items. Put
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
The final two,
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
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.