UI/UX Atlas
Accessibility Intermediate

ARIA & the Authoring Practices Guide (APG)

Knowing when — and when not — to reach for ARIA separates accessible components from fragile ones; the APG is your authoritative field guide.

8 min read

The full lesson

ARIA (Accessible Rich Internet Applications) is a specification that lets developers send extra meaning to assistive technologies when plain HTML isn’t enough. Used well, ARIA connects modern interactive UIs to screen readers, voice-control software, and switch-access devices. Used carelessly, it creates the appearance of accessibility while silently breaking the experience for the very users it should help. The W3C’s ARIA Authoring Practices Guide (APG) exists to keep you on the right side of that line.

What ARIA Actually Is

ARIA is a set of HTML attributes — roles, states, and properties — that annotate the accessibility tree. The accessibility tree is a separate representation of the DOM that operating systems expose to assistive technologies (AT). When a browser renders a page, it builds two structures from the same markup: the visual render tree and the accessibility tree. Screen readers like NVDA, JAWS, VoiceOver, and TalkBack read from the accessibility tree, not the screen.

ARIA does not add keyboard behavior. It does not change visual appearance. It does not make non-interactive elements interactive. It is purely semantic annotation — a label your browser sends to the OS accessibility API.

Three attribute families make up the full ARIA vocabulary:

FamilyPurposeExample
RolesWhat kind of element this isrole="dialog", role="tabpanel"
PropertiesStatic characteristicsaria-label, aria-labelledby, aria-required
StatesDynamic, changing conditionsaria-expanded, aria-checked, aria-busy

The Five Rules of ARIA Use

The W3C formalized five rules for ARIA use. Every practitioner should know them.

  1. Use native HTML semantics first. Prefer nav, main, button, input, and similar elements before reaching for ARIA.
  2. Do not change native semantics unless you have to. Avoid role="heading" on a span when an h2 already exists.
  3. All interactive ARIA controls must be keyboard-operable. If you give something role="slider", you must implement arrow-key behavior.
  4. Do not hide focusable elements. Do not use aria-hidden="true" on an element that is focused or contains a focused element.
  5. Interactive elements must have accessible names. Every button, input, and control needs a clear text label — via visible text, aria-label, or aria-labelledby.

What the APG Is and How to Read It

The ARIA Authoring Practices Guide (APG) is the W3C’s reference for building common UI patterns accessibly. It lives at w3.org/WAI/ARIA/apg/ and is maintained by the ARIA Working Group. Think of it as the official cookbook. For each pattern — accordion, carousel, combobox, dialog, disclosure, menu, tabs, tree, and dozens more — it documents:

  • Which ARIA roles, states, and properties to use
  • The expected keyboard interaction model
  • A working code example you can adapt
  • Links to the underlying ARIA specification

The APG is authoritative but not infallible. It describes one correct approach, not the only valid one. Some patterns (notably the menu and menubar roles) carry heavy interaction expectations inherited from desktop applications that can feel out of place on the web. Always check real AT compatibility data — the NVDA/JAWS/VoiceOver/Safari matrix at a11ysupport.io — before adopting a pattern wholesale.

Landmark Regions

Before reaching for widget patterns, make sure the page’s landmark structure is correct. ARIA landmarks let AT users jump directly to main content, navigation, or search:

  • role="banner" / header (top-level only)
  • role="navigation" / nav
  • role="main" / main
  • role="complementary" / aside
  • role="contentinfo" / footer (top-level only)
  • role="search" (no native equivalent — use role="search" on a form or wrapping div)

When the same landmark appears more than once on a page, give each one a unique accessible name via aria-label or aria-labelledby. That way AT users can tell “Primary navigation” apart from “Breadcrumb navigation.”

Naming Controls Correctly

Accessible names are the most common ARIA failure. The browser computes an accessible name for each element using the Accessible Name and Description Computation algorithm, which checks sources in this priority order:

  1. aria-labelledby (references another element’s visible text — strongest signal)
  2. aria-label (inline string — use when no visible text exists)
  3. Native labeling: label element, alt on images, caption on tables
  4. title attribute (last resort; avoid for primary labeling)
  5. Text content of the element itself (for buttons and links)

A common mistake is adding aria-label to an element that already has visible text. This creates a mismatch: sighted users read “Add to cart” while AT users hear “Purchase item.” That inconsistency hurts discoverability and makes testing harder. When you override with aria-label, match or include the visible text.

Dialog and Focus Management

Dialogs are where ARIA use most commonly goes wrong. The APG’s dialog pattern requires:

  • role="dialog" on the container
  • aria-modal="true" when the dialog is modal (blocks interaction with background content)
  • An accessible name via aria-labelledby pointing to the dialog’s visible heading
  • Focus moved into the dialog when it opens (to the first focusable element or the dialog itself)
  • Focus trapped inside the dialog while it is open
  • Focus returned to the trigger element when the dialog closes

The modern approach for focus trapping is the HTML inert attribute. Setting inert on all sibling elements of an open dialog removes them from tab order, keyboard interaction, and the accessibility tree at once — no JavaScript focus-trap loop needed. This replaced the older pattern of manually intercepting Tab and Shift+Tab keydown events, which was fragile and often failed with dynamically inserted content.

<!-- When dialog opens, make the rest of the page inert -->
<main inert>…page content…</main>
<aside inert>…sidebar…</aside>

<div role="dialog" aria-modal="true" aria-labelledby="dlg-title">
  <h2 id="dlg-title">Confirm deletion</h2>
  <p>This action cannot be undone.</p>
  <button>Delete</button>
  <button>Cancel</button>
</div>

Live Regions: Communicating Dynamic Changes

When content updates without a page reload — form validation messages, toast notifications, chat messages, progress indicators — AT users receive no signal unless you annotate the region.

ARIA live regions solve this with two key attributes:

  • aria-live="polite" — announces the change after the current AT utterance finishes. Use for non-urgent updates like status messages or search result counts.
  • aria-live="assertive" — interrupts immediately. Use only for critical, time-sensitive information such as error alerts that block progress. Overuse causes AT to interrupt itself repeatedly and becomes noise.

The convenience roles role="status" (polite) and role="alert" (assertive) are shorthand equivalents. Prefer them for clarity.

One critical detail: the live region element must exist in the DOM before content is injected into it. Dynamically creating a live region and populating it in the same render cycle is unreliable. Many AT fail to register the announcement because they observed no change from a pre-existing baseline.

<!-- Declare empty region on page load -->
<div role="status" aria-live="polite" aria-atomic="true" id="form-status"></div>

<!-- Later, inject message via JavaScript -->
document.getElementById('form-status').textContent = '3 results found';

Common Patterns: Tabs, Accordions, and Comboboxes

Tabs

The APG tab pattern uses role="tablist", role="tab", and role="tabpanel". Tabs use arrow keys for selection — not Tab, which moves focus in and out of the tablist. The selected tab has aria-selected="true" and its panel has no hidden attribute. Inactive tabs have aria-selected="false" and their panels use hidden or aria-hidden="true".

Two keyboard models exist:

  • Automatic selection — arrow keys move focus and immediately activate the tab.
  • Manual selection — arrow keys move focus only; Space or Enter activates.

The APG recommends automatic selection for most cases. Manual selection is appropriate when tab content is expensive to load.

Accordions

Accordions use button elements as the disclosure trigger — not div or dt. The button’s aria-expanded attribute is "true" when the panel is open and "false" when closed. The controlled panel can use aria-hidden or hidden when collapsed. No special role is needed on the panel itself.

Comboboxes

The combobox is the most complex APG pattern and the most frequently mis-implemented. A combobox is an input that controls a listbox popup. Key requirements:

  • The input has role="combobox", aria-expanded, and aria-controls pointing to the listbox.
  • The listbox has role="listbox" and each option has role="option".
  • The active option gets aria-selected="true" and the input gets aria-activedescendant pointing to it.
  • The popup is dismissed with Escape; selection completes with Enter.

Do

Use aria-describedby to link form inputs to their error messages and helper text. This connects the description to the field semantically rather than relying on proximity in the DOM.

Use aria-required="true" alongside the native required attribute — screen readers announce it, and browsers enforce it natively for basic validation.

Don't

Don’t use aria-placeholder as a substitute for a persistent label. When placeholder text disappears on focus, users lose the field’s purpose.

Don’t stack multiple aria-labelledby IDs indiscriminately. The algorithm supports space-separated ID lists, but the resulting concatenation can sound awkward. Test with real AT.

What ARIA Cannot Fix

ARIA is corrective, not constructive. Several accessibility problems are outside its scope:

  • Cognitive load — ARIA cannot simplify confusing instructions or improve content hierarchy.
  • Color contrast — ARIA has no contrast attributes. Solve this in design tokens. WCAG 2.2 AA requires 4.5:1 for normal text and 3:1 for large text.
  • Touch target size — WCAG 2.2 criterion 2.5.8 requires at least 24x24 CSS pixels. ARIA cannot enlarge targets.
  • Focus visibilityoutline: none with no alternative focus indicator is a WCAG 2.4.11 failure. ARIA cannot replace visible focus styles.
  • Motion sensitivity — the prefers-reduced-motion CSS media query is the correct tool, not ARIA.

Treating ARIA as a catch-all patch for poor semantic structure leads to layers of compensatory annotation that become impossible to maintain. The correct order is: semantic HTML first, then WCAG-compliant visual design, then ARIA only where a gap remains.

Modern Practices vs. Outdated Habits

Modern practiceOutdated habit
inert attribute for focus trappingManual Tab/Shift+Tab keydown intercept loops
role="status" / role="alert" for dynamic messagesNo live region; AT users miss async updates
Native button and input elementsDiv soup with role="button" and manually wired click handlers
WCAG 2.2 target criteria as baselineTargeting WCAG 2.0; ignoring 2.5.8 Target Size and 2.4.11 Focus Not Obscured
aria-describedby linking errors to inputsError text rendered near (but not associated with) the field
tabindex="0" to add custom elements to tab order; tabindex="-1" to enable programmatic focustabindex="1" or higher (positive tabindex breaks natural tab order for all users)