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.
- role describes what an element is supposed to represent, such as a button, dialog, or navigation region.
- Attributes that start with aria- describe accessibility properties such as labels, states, and relationships.
- ARIA is especially useful when you build custom components that do not have a built-in semantic element.
- Native HTML elements already provide many of the same signals, so ARIA is often unnecessary for standard controls.
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:
- what an element does, such as opening a menu or expanding a section
- whether something is selected, expanded, busy, or hidden
- how pieces of a widget relate to each other, such as a tab and its tabpanel
- which text should be announced as the accessible name of a control
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
- Custom dialogs that need role="dialog", a label, and focus management.
- Accordion buttons that need aria-expanded and a relationship to the content they control.
- Tabs, tab panels, and menus that need roles and state attributes to communicate selection and visibility.
- Icon-only controls that need accessible names through aria-label or aria-labelledby.
- Live status messages such as form saving, loading, or validation feedback using aria-live.
- Complex page landmarks where labels help distinguish multiple navigation, search, or complementary regions.
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
- ARIA does not add native keyboard behavior. A custom control still needs correct keyboard interaction.
- ARIA does not make an element visually accessible. Color contrast, focus styling, and layout still matter.
- Some roles are not allowed on some elements, and browsers may ignore invalid combinations.
- Overusing ARIA can make a page harder to maintain and can confuse assistive technologies.
- Screen reader output varies by browser and device, so testing matters even when the code looks correct.
- Using aria-hidden on a parent can hide all of its descendants from assistive technology, including controls you meant to keep available.
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
- ARIA adds accessibility meaning when native HTML is not enough.
- role tells assistive technology what an element represents.
- aria-* attributes describe names, states, and relationships.
- Native semantic HTML should be your first choice.
- Use ARIA carefully, accurately, and only when it improves the user experience.
11. Practice Exercise
- Create a FAQ item with a question button and a hidden answer panel.
- Add the correct ARIA attributes so the button announces whether the answer is expanded.
- Make sure the question has a clear accessible name.
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.