ARIA Roles and Attributes in HTML: Complete Accessibility Guide

ARIA roles and attributes let you add accessibility information to HTML so assistive technologies can understand what an element is, what state it is in, and how users can interact with it. Used well, they make custom controls and dynamic interfaces more usable for people who rely on screen readers, keyboard navigation, and other assistive tools.

Quick answer: Use native HTML elements first, and add ARIA only when HTML alone cannot express the needed meaning or state. The most important rule is that ARIA should support semantic HTML, not replace it.

Difficulty: Beginner

You'll understand this better if you know: basic HTML elements, how forms and buttons work, and the difference between visible text and semantic meaning.

1. What Is ARIA Roles & Attributes?

ARIA stands for Accessible Rich Internet Applications. In HTML, ARIA gives extra information to browsers and assistive technologies about an element’s purpose, name, state, or relationship to other content.

A common comparison is button versus a div with ARIA. A real button is usually better because it already behaves correctly for keyboard users and screen readers. A div only becomes accessible if you add the right role, states, keyboard handling, and labels.

2. Why ARIA Roles & Attributes Matter

Many modern interfaces include custom menus, tabs, dialogs, accordions, and live updates. These patterns often need more than plain text and nesting to communicate meaning to assistive technology.

ARIA matters because it can help users understand:

Without ARIA, a custom widget may look correct visually but be confusing or unusable for screen reader users. With ARIA used correctly, the page becomes much clearer without changing its visual design.

3. Basic Syntax or Core Idea

Using a role

The role attribute tells assistive technologies what kind of UI element something behaves like.

<div role="button" tabindex="0">Open details</div>

This example tells the browser and assistive tools that the element acts like a button. In practice, you would still need keyboard support and an accessible name, and a real button element is usually better.

Using an ARIA attribute

An aria- attribute provides extra information about the element.

<button aria-expanded="false" aria-controls="menu1">Menu</button>

Here, aria-expanded describes whether the controlled content is open or closed, and aria-controls points to the related element by ID.

Providing an accessible name

Some controls need a label even if visible text is not enough.

<button aria-label="Close dialog"></button>

This gives the button a readable name for assistive technologies, even though it has no visible text.

4. Step-by-Step Examples

Example 1: Labeling an icon-only button

Icon buttons often have no visible text, so they need an accessible name. This is one of the most common uses of ARIA.

<button aria-label="Search">
  <span aria-hidden="true">🔍</span>
</button>

The button gets its name from aria-label, while the icon is hidden from assistive technology with aria-hidden="true".

Example 2: Marking a collapsible section

When a section can expand and collapse, use state attributes to describe what is happening.

<button aria-expanded="true" aria-controls="faq-1">What is ARIA?</button>
<div id="faq-1">
  <p>ARIA helps accessibility tools understand rich interfaces.</p>
</div>

The state tells users whether the content is currently open, and the relationship connects the control to the content it affects.

Example 3: Describing a live status message

Status text that changes dynamically can use a live region so screen readers announce updates.

<p aria-live="polite">Saving changes...</p>

When the text changes, assistive technology can announce the update without forcing immediate interruption.

Example 4: Identifying a navigation region

Most navigation should already use the semantic nav element, but ARIA roles can still help in more complex structures or older patterns.

<nav aria-label="Primary">
  <a href="/">Home</a>
  <a href="/pricing">Pricing</a>
</nav>

The landmark is made more specific with aria-label, which helps users distinguish it from other navigation regions.

5. Practical Use Cases

These uses are strongest when the visual interface is richer than standard HTML controls and you need to preserve meaning for non-visual users.

6. Common Mistakes

Mistake 1: Using ARIA when native HTML already solves the problem

Many beginners add roles to elements that already have the correct semantics. This often creates unnecessary complexity and can even override built-in behavior.

Problem: A native button already announces itself correctly and works with keyboard input, so replacing it with a generic element plus ARIA is usually a step backward.

<div role="button" tabindex="0">Save</div>

Fix: Use the native control whenever possible.

<button>Save</button>

The corrected version works better because native HTML already includes the right semantics and interaction model.

Mistake 2: Adding ARIA without the matching state or relationship

ARIA roles and state attributes often work in pairs. If you announce that something expands, the controlled content and state must match the real interface.

Problem: The button claims it controls something, but the target ID is missing or the state does not reflect what users actually see. Screen reader users may hear inaccurate information.

<button aria-expanded="true" aria-controls="more-info">More details</button>

Fix: Make sure the controlled element exists and the state matches the visible state.

<button aria-expanded="false" aria-controls="more-info">More details</button>
<div id="more-info" hidden>
  <p>Extra information appears here.</p>
</div>

The corrected version keeps the accessibility state aligned with what users can actually experience.

Mistake 3: Hiding important content with aria-hidden

aria-hidden="true" removes content from the accessibility tree. That is useful for decorative icons, but not for meaningful text or interactive controls.

Problem: If you hide the only text label or a focusable control, screen reader users may not be able to discover or use it.

<button>
  <span aria-hidden="true">Submit</span>
</button>

Fix: Hide only decorative content, and keep the accessible name available.

<button aria-label="Submit">
  <span aria-hidden="true">✓</span>
</button>

The corrected version works because the accessible name remains available while the decorative icon stays hidden.

7. Best Practices

Practice 1: Prefer semantic HTML before ARIA

The best accessibility improvement is usually choosing the right element. A semantic element gives you built-in keyboard behavior, roles, and states for free.

<a href="/contact">Contact us</a>

This is better than building a link from a generic element because it already behaves like a link.

Practice 2: Use the most specific naming method available

If a visible label already exists, connect it with aria-labelledby instead of duplicating text in aria-label.

<h2 id="profile-title">Profile</h2>
<section aria-labelledby="profile-title">
  <p>Account details and settings.</p>
</section>

This keeps the visible text and the accessible name in sync.

Practice 3: Keep ARIA states accurate at all times

States such as aria-expanded, aria-selected, and aria-checked should always reflect the current UI state.

<button aria-expanded="false">Filters</button>

If the panel opens, update the state so assistive technology receives the same truth that sighted users see.

8. Limitations and Edge Cases

A common “not working” complaint is that a role seems correct but the screen reader still does not behave as expected. In many cases, the missing piece is not the ARIA itself but a semantic mismatch, a missing label, or absent keyboard support.

9. Practical Mini Project

Here is a small accessible disclosure component built with semantic HTML and ARIA state. It shows the basic pattern for a button that reveals extra content.

<section aria-labelledby="shipping-heading">
  <h2 id="shipping-heading">Shipping information</h2>
  <button type="button" aria-expanded="false" aria-controls="shipping-details">
    View shipping details
  </button>
  <div id="shipping-details" hidden>
    <p>Orders usually arrive in 3 to 5 business days.</p>
  </div>
</section>

This pattern works well because the heading labels the section, the button announces whether the content is open, and the hidden panel stays out of view until needed. In a real application, you would update aria-expanded and the hidden attribute together so the visual and accessibility states always match.

10. Key Points

11. Practice Exercise

Expected output: A button that reports its expanded or collapsed state and a related content panel that can be identified by assistive technology.

Hint: Use aria-expanded on the button, connect it to the answer with aria-controls, and keep the answer in a real container with an id.

<section aria-labelledby="faq-heading">
  <h2 id="faq-heading">Frequently Asked Questions</h2>

  <button type="button" aria-expanded="false" aria-controls="faq-answer-1">
    What is ARIA used for?
  </button>

  <div id="faq-answer-1" hidden>
    <p>ARIA helps describe custom or dynamic UI patterns to assistive technologies.</p>
  </div>
</section>

This solution shows the core pattern: a labeled region, a button with a truthfully updated state, and a controlled panel that can be revealed when needed.

12. Final Summary

ARIA roles and attributes are a powerful part of accessible HTML, but they work best when they enhance semantic markup rather than replace it. If an HTML element already does the right job, prefer it over a custom solution.

When you do need ARIA, focus on three things: accurate roles, correct labels, and state values that match the real user interface. That combination helps screen reader users understand what is on the page and how to use it.

For the next step, practice auditing a simple page and replacing unnecessary ARIA with better semantic HTML, then add ARIA only where a custom component truly needs it.