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:

  <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>
This multi-value 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">

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:

  1. Roles like scrollbar, log, or directory describe things HTML can’t. A custom JavaScript scrollbar needs role="scrollbar", and tables of contents should look like <nav role="directory">.

  2. 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.

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, and alertdialog 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 like log, but more like a stock ticker. It's described as “non-essential information that changes frequently”, and differs from log in that it doesn’t care about order. It’s the <ul> to log’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, and menuitemradio describe how menu controls are laid out. These will eventually be obsoleted by <menu>.

  • tablist, tab, and tabpanel 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 identical names, if you can’t use <fieldset>.

  • grid describes interactive tabular data, like spreadsheets.

  • tree describes interactive tree diagrams (filesystems, expandable JSON, etc.), and treegrid 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 the title 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, and rowheader describe 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.

  • 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 the aria-level attribute, described later.

  • img = <img>. Used for <div>s with a background-image (a common method before <picture> and srcset), ASCII art, or other elements that should be interpreted like an image. Say, inline <svg>.

  • list and listitem 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 with aria-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 with role="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 inside application. If your app contains documents, like a webmail client, you can restore normal assistive navigation with role="document".

  • banner is for sitewide <header>s. Site name, tagline, logos, top navigation, etc. Do not use more than once inside each role="document" or role="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 per role="document" or role="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 like aria-checked, but for toggle buttons and switches.

  • aria-expanded indicates the element behaves like <details>/<summary>. When it’s expanded, use aria-expanded="true". When collapsed, use false.

  • aria-hidden is 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 hidden 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 universal alt attribute. If you use it like aria-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 like aria-level="2". It’s for nested grid, heading, listitem, row, and tablist roles: complex tables, subheadings, nested lists, etc.

  • aria-multiline describes if a text input allows single or multiple lines. It’s how you specify if role="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 for scrollbar, separator, or slider to indicate if they’re vertical or horizontal (default).

  • aria-readonly is like readonly 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 the columnheader and rowheader roles.

  • aria-valuemax, aria-valuemin, and aria-valuenow work like the min, max, and value 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 set aria-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 apply aria-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 ids 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">

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:

    <img src="barack-obama.jpg" alt="" aria-labelledby="caption">
    <figcaption id="caption">A photograph of Barack Obama.</figcaption>

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 roles 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.