RatedWithAI

RatedWithAI

Accessibility scanner

ARIA Labels & Roles Accessibility Guide 2026: WAI-ARIA for Web Developers

WAI-ARIA (Accessible Rich Internet Applications) is how you make dynamic web content, custom widgets, and complex interfaces accessible to screen reader users. But ARIA is widely misused — adding it incorrectly makes things worse, not better. This guide covers the most important ARIA attributes, roles, and states, with examples of correct and incorrect usage.

The First Rule of ARIA

If you can use a native HTML element or attribute with the semantics and behavior you require, use it instead of repurposing an element and adding ARIA. Native HTML elements are accessible by default. ARIA is a repair mechanism — use it when HTML can't express the semantics you need.

Accessible Names: aria-label, aria-labelledby, aria-describedby

Every interactive element needs an accessible name — the text a screen reader announces when the element receives focus. ARIA provides three main attributes for naming and describing elements:

aria-label

Provides an accessible name as a string value directly in the attribute. The value is read aloud by screen readers instead of the element's visible text content.

Use when: An interactive element has no visible text label — typically icon buttons, close buttons, and navigation elements that rely on visual context.

<!-- Icon button with no visible text -->
<button aria-label="Close dialog">
<svg aria-hidden="true">...</svg>
</button>
<!-- Screen reader announces: "Close dialog, button" -->

aria-labelledby

Points to the ID of another element whose visible text becomes the accessible name. Overrides aria-label if both are present.

Use when: A visible text label already exists on the page that should serve as the accessible name. This keeps the accessible name in sync with the visual label automatically. Multiple IDs can be referenced to concatenate text from multiple elements.

<!-- Dialog with a heading as its accessible name -->
<div role="dialog" aria-labelledby="dialog-title" aria-modal="true">
<h2 id="dialog-title">Confirm Deletion</h2>
<p>Are you sure you want to delete this item?</p>
<button>Cancel</button>
<button>Delete</button>
</div>
<!-- Screen reader announces: "Confirm Deletion, dialog" when focus enters -->

aria-describedby

Points to supplementary descriptive text. Unlike aria-labelledby, this doesn't provide the accessible name — it provides additional context announced after the name. Used for error messages, hints, and additional instructions.

<!-- Input with associated error message -->
<label for="email">Email</label>
<input type="email" id="email" aria-describedby="email-error" aria-invalid="true" />
<span id="email-error" role="alert">Please enter a valid email address.</span>
<!-- Screen reader: "Email, edit text, invalid, Please enter a valid email address." -->

ARIA Landmark Roles

Landmark roles divide the page into named regions that screen reader users can jump between using keyboard shortcuts. Most are automatically applied by HTML5 semantic elements — you don't need to add them explicitly unless using non-semantic elements or needing multiple labeled instances.

Landmark roleHTML elementPurpose
banner<header>Site header — typically containing logo, site name, and primary nav
navigation<nav>Navigation links — use aria-label to distinguish multiple navs
main<main>Primary content area — only one per page
complementary<aside>Sidebar or supplementary content tangentially related to main
contentinfo<footer>Footer with copyright, links, legal info
search<form role="search">Search form — add aria-label='Site search'
form<form>Significant form — only applied when form has an accessible name
region<section>Section with a heading — only a landmark when given an accessible name

When a page has multiple navigation landmarks (e.g., primary nav and footer nav), distinguish them with aria-label: <nav aria-label='Primary navigation'> and <nav aria-label='Footer navigation'>. Screen readers announce "Primary navigation, navigation" and "Footer navigation, navigation" respectively.

ARIA Live Regions

Live regions announce dynamic content changes to screen readers. Without them, content that appears or updates without a page reload — form validation errors, notifications, search results, chat messages — is silently invisible to screen reader users.

aria-live Values

aria-live="polite"role="status"

Waits for the user to finish their current interaction before announcing. Use for most dynamic content — form hints, status updates, non-urgent notifications.

Example use: Form saved successfully. | 3 search results found.

aria-live="assertive"role="alert"

Interrupts the user immediately, even if they're in the middle of reading or interacting. Use sparingly — only for time-sensitive critical alerts.

Example use: Session expiring in 60 seconds. | Payment declined.

aria-live="off"

Default behavior — changes are not announced. Effectively disables live region behavior.

Live Region Best Practices

  • Add the live region to the DOM on page load — before injecting content into it. Some screen readers only register live regions that exist when the page initializes.
  • Keep live region content concise. Screen readers read the full text of any changed element, so don't place a large section in a live region.
  • Use aria-atomic="true" to announce the entire region when any part changes (not just the changed part). Useful for status messages where partial announcements would be confusing.
  • Don't use assertive for anything that isn't genuinely critical. Constant interruptions degrade the screen reader experience significantly.

Common ARIA States and Properties

aria-expandedValues: true / false

Whether a collapsible region (accordion, dropdown, disclosure) is expanded or collapsed. Apply to the trigger element, not the content region.

aria-hiddenValues: true / false

Removes element from the accessibility tree. Use for decorative icons, duplicate text, and visually redundant UI. Never apply to focusable elements.

aria-invalidValues: true / false / grammar / spelling

Marks a form input as having a validation error. Pair with aria-describedby pointing to the error message.

aria-requiredValues: true / false

Marks a form input as required. The HTML required attribute implies this automatically.

aria-disabledValues: true / false

Marks an element as disabled. Unlike HTML disabled, aria-disabled keeps the element in the tab order so screen reader users can discover it and understand why it can't be activated.

aria-selectedValues: true / false / undefined

Indicates which option is selected in a listbox, grid, or tab widget. Not for checkboxes (use aria-checked) or buttons (use aria-pressed).

aria-checkedValues: true / false / mixed

State of a checkbox or radio button. mixed is for tri-state checkboxes (e.g., 'select all' in a partially-selected group).

aria-pressedValues: true / false / mixed

Toggle button state. A button that stays 'on' after being clicked (like Bold in a text editor) should use role='button' aria-pressed='true/false'.

aria-currentValues: page / step / location / date / time / true / false

Marks the current item in a navigation or process. Use aria-current='page' on the active nav link.

aria-modalValues: true / false

Tells screen readers that a dialog is modal and should restrict interaction to its contents. Requires focus to be trapped inside the dialog while it's open.

Common ARIA Mistakes to Avoid

Using ARIA roles on native HTML elementsHigh

Adding role="button" to a <div> doesn't make it a button — you must also handle keydown events, focus management, and visual states. Use <button> instead.

aria-hidden on focusable elementsCritical

If an element with aria-hidden='true' is in the tab order, keyboard users can focus it but screen readers won't announce anything — creating a silent, confusing tab stop.

Duplicate ARIA that overrides native semanticsLow

Adding role="heading" to an <h2> is redundant. Adding role="list" to a <ul> is redundant. These add noise and can confuse screen readers.

Dynamic ARIA labels that don't updateHigh

A button with aria-label="Play video" that doesn't update to "Pause video" when clicked creates a mismatch between what screen readers announce and what the control does.

Missing aria-live containersMedium

Adding aria-live to a container only works if the container is in the DOM when the page loads. Adding aria-live dynamically at the same time as injecting content often fails.

Using aria-label to add hidden extra text to linksMedium

aria-label overrides visible text completely — aria-label="Click here for more information about pricing" on a "Pricing" link replaces "Pricing", not supplements it. Screen readers will say the aria-label text, ignoring the visible "Pricing".

Sponsored

Also audit your site's full technical health

SEMrush Site Audit checks 130+ issues — missing alt text, broken links, slow pages. Free crawl up to 100 pages, no credit card required.

Try SEMrush Free →

Frequently Asked Questions

What is the difference between aria-label and aria-labelledby?

aria-label provides an accessible name directly as a string value in the attribute. aria-labelledby points to the ID of another element whose visible text becomes the accessible name. Use aria-labelledby when a visible label already exists on the page — it reuses visible text, keeping the accessible name in sync with the visual UI. Use aria-label when no suitable visible text exists (e.g., an icon button).

When should I NOT use ARIA?

Don't use ARIA when native HTML already provides the semantics. A <button> is already a button — adding role='button' to a <div> requires you to also handle keyboard events, focus, and state manually. The first rule of ARIA is: if you can use a native HTML element with the correct semantics, do that instead. ARIA fixes what HTML can't express — it's not a shortcut.

What are ARIA landmark roles?

ARIA landmark roles define regions of a page that screen reader users can jump between. The main landmarks are: banner (header), navigation (nav), main, complementary (aside), contentinfo (footer), search, form, and region. Most are implicit in HTML5 elements — <header> gets banner, <nav> gets navigation, <main> gets main, <footer> gets contentinfo automatically. Explicit role attributes are needed when using non-semantic elements or when you have multiple instances of the same landmark.

What is an ARIA live region?

An ARIA live region is an area of the page that announces dynamic content changes to screen readers without requiring the user to navigate there. aria-live='polite' waits for the user to finish their current action before announcing. aria-live='assertive' interrupts immediately — use only for critical alerts. role='status' is equivalent to aria-live='polite'. role='alert' is equivalent to aria-live='assertive'. Use live regions for form errors injected dynamically, toast notifications, chat messages, and search results that load without a page refresh.

What does aria-hidden do?

aria-hidden='true' removes an element and all its children from the accessibility tree — screen readers skip it entirely. Use it for decorative icons, repeated or redundant content, and visual elements that would create noise (like duplicate labels). Never apply aria-hidden to focusable elements — keyboard users can still reach them but screen readers won't announce anything, creating a confusing silent tab stop.

Scan for ARIA Errors on Your Website

RatedWithAI's accessibility scanner detects broken ARIA label associations, missing roles, invalid ARIA states, and other common ARIA implementation errors. Get a free report showing exactly what needs to be fixed.