AccessScanRun a free scan

Guide

ARIA Best Practices: Use Native HTML First, ARIA Second

WAI-ARIA (Accessible Rich Internet Applications) lets you describe interface semantics to assistive technology when HTML alone cannot. It is powerful and routinely misused. Accessibility surveys of large website samples regularly find that pages using ARIA tend to have more detectable errors than pages without it, because ARIA changes how a screen reader interprets your markup without changing how it looks or behaves.

These ARIA best practices are built around a simple discipline: reach for a native HTML element first, and only add ARIA when no native element does the job. Below we cover the first rule of ARIA, the mistakes that ship most often, how roles, states, and properties actually work, and the cases where ARIA is genuinely the right tool.

The First Rule of ARIA: Don't Use ARIA

The W3C's own authoring guidance opens with this: if you can use a native HTML element or attribute with the semantics and behavior you need already built in, do that instead of repurposing an element and adding an ARIA role, state, or property. A native <button> ships with a role, keyboard focusability, Enter and Space activation, and a disabled state for free. A <div role="button"> ships with none of that — you inherit responsibility for every behavior the browser used to give you.

This matters because ARIA is a promise to assistive technology, not an instruction to the browser. role="button" tells a screen reader "announce this as a button." It does not make the element focusable, does not add keyboard handlers, and does not change a single pixel on screen. If you make that promise and don't implement the behavior, you have built something that announces itself as a button but cannot be operated like one — worse than no ARIA at all.

Native elements also stay correct across browser and screen reader updates, high-contrast modes, and forced-colors themes. Custom widgets have to be re-tested against all of them. Before building a custom component, confirm no native element already covers the pattern — most of the time, one does.

Roles, States, and Properties: The Three Building Blocks

ARIA has exactly three kinds of attributes, and knowing which is which prevents most confusion.

  • Roles define what a thing is. role="tablist", role="dialog", role="alert". A role is usually set once and not changed. Critically, a role overrides the element's native semantics: <h2 role="presentation"> is no longer a heading to assistive tech.
  • Properties describe relationships and features that rarely change. aria-labelledby points to the element that names this one; aria-describedby points to extra description; aria-controls names the element this widget operates; aria-required marks a field mandatory.
  • States describe the current, changing condition of a widget. aria-expanded toggles true/false as a disclosure opens; aria-checked tracks a custom checkbox; aria-selected marks the active tab; aria-disabled and aria-hidden gate interaction and exposure. These you update in JavaScript as the user interacts.

A common failure is setting a state once and never updating it. aria-expanded="false" hard-coded on a menu button that does open is a lie told to every screen reader user. If your script toggles a CSS class to show the panel, it must toggle the matching ARIA state in the same handler. Treat the visible state and the ARIA state as one unit that always changes together.

Common ARIA Mistakes That Ship to Production

Most ARIA bugs cluster into a handful of patterns. Watch for these specifically:

  • Redundant roles on native elements: <button role="button"> or <nav role="navigation"> add nothing and signal copy-paste habits that often hide real errors elsewhere.
  • aria-label on elements that don't support a name, like a plain <div> or <span> with no role — the label is silently ignored. Names work on interactive and landmark roles, not arbitrary containers.
  • aria-hidden="true" on a focusable element. The element vanishes from the accessibility tree but still takes keyboard focus, stranding screen reader users on a control that announces nothing.
  • Replacing a real label with aria-label that differs from the visible text. Voice-control users say what they see; if the visible text is "Submit" but aria-label is "Send form," the spoken command fails (WCAG 2.5.3 Label in Name, Level A).
  • Using role="alert" or aria-live on content that is present at page load. Live regions only announce changes after the page settles, so static markup in them is never spoken.
  • Custom widgets with correct ARIA but no keyboard support. A role="tab" with no arrow-key handling is not a tab to anyone using a keyboard.

Forms are where these mistakes do the most damage. A native <label> tied to an input with for/id is more robust than any ARIA equivalent. Keyboard operability for every custom control is non-negotiable — the rules are laid out in our keyboard accessibility guide.

When ARIA Actually Helps

ARIA earns its place when HTML genuinely has no element for the job. Real use cases include:

  • Composite widgets HTML doesn't provide: tabs, tree views, comboboxes, and carousels. Follow the published patterns in the ARIA Authoring Practices Guide rather than inventing your own role combinations.
  • Live regions for dynamic updates: aria-live="polite" for a "3 results found" status, aria-live="assertive" or role="alert" for an error that must interrupt. This is one of the few things HTML cannot express on its own.
  • Naming and describing: aria-labelledby and aria-describedby to wire an accessible name to an icon-only button or connect a form field to its error and hint text.
  • Relationships across the DOM: aria-controls, aria-owns, and aria-activedescendant to express structure that the HTML nesting cannot, such as a listbox controlled by a separate text input.
  • State the visuals already imply: aria-current="page" on the active nav link, aria-expanded on a disclosure, aria-pressed on a toggle button.

Even here, the discipline holds: add the minimum ARIA the pattern requires, test it with an actual screen reader, and verify keyboard operation. A combobox with perfect ARIA and broken arrow keys still fails. Pair every ARIA addition with manual testing, because automated tools catch redundant roles and invalid values but cannot confirm a widget actually works.

ARIA and Compliance: Robust Means Machine-Readable

The fourth WCAG principle, Robust, exists largely because of ARIA: interface state and roles must be programmatically determinable so assistive technology can interpret them reliably. WCAG 2.2 removed the old 4.1.1 Parsing criterion, since modern browsers handle malformed markup gracefully, but 4.1.2 Name, Role, Value (Level A) remains central — every custom control must expose a correct name, role, and current state.

This is not just good practice. The European Accessibility Act (Directive (EU) 2019/882) has applied since 28 June 2025, and its baseline is WCAG 2.2 Level A and AA referenced through EN 301 549. Custom widgets that announce the wrong role or never update their state are concrete failures against that baseline, and non-compliance can carry penalties. A free automated scan with AccessScan flags common ARIA defects — redundant roles, invalid values, and focusable aria-hidden elements — in seconds, giving you a prioritized starting point before manual screen reader testing.

Check your site against AccessScan

See your issues ranked by impact in seconds — free.

Run a free accessibility scan

FAQ

What is the first rule of ARIA?

If a native HTML element or attribute already provides the semantics and behavior you need, use it instead of repurposing a generic element and adding an ARIA role. Native elements bring built-in focus, keyboard handling, and states that ARIA alone does not.

Does ARIA change how an element looks or behaves?

No. ARIA only changes what assistive technology announces. It adds no styling, no focusability, and no keyboard behavior. If you give a div role="button", you must still add tabindex, keyboard handlers, and a focus style yourself.

What is the difference between ARIA roles, states, and properties?

Roles define what an element is (role="dialog") and are usually set once. Properties describe stable relationships or features (aria-labelledby, aria-required). States describe the current, changing condition (aria-expanded, aria-checked) and must be updated in JavaScript as the user interacts.

Is using ARIA required for accessibility compliance?

Not by itself. Well-structured native HTML meets most WCAG 2.2 Level A and AA requirements without ARIA. ARIA is needed for patterns HTML cannot express, such as tabs or live regions, but incorrect ARIA causes failures, so use the minimum the pattern requires and test it.

Why do pages with ARIA often have more accessibility errors?

Because ARIA overrides native semantics and makes promises about behavior that developers then fail to implement, such as a role="button" with no keyboard support or an aria-expanded value that never updates. Used carelessly, ARIA introduces errors that native HTML would have prevented.

More guides