Skip to main content

HTML Accessibility - ARIA Roles and Attributes

Answer

ARIA (Accessible Rich Internet Applications) provides attributes that enhance HTML semantics for assistive technologies like screen readers.

ARIA Categories

Key Principles

Essential ARIA Roles

Landmark Roles

<!-- Landmarks help screen reader users navigate -->
<header role="banner">
<nav role="navigation" aria-label="Main">
<!-- Navigation links -->
</nav>
</header>

<main role="main">
<article role="article">
<!-- Content -->
</article>
</main>

<aside role="complementary">
<!-- Sidebar -->
</aside>

<footer role="contentinfo">
<!-- Footer content -->
</footer>

Note: HTML5 semantic elements have implicit roles, so <header> already has role="banner". Add explicit roles only when needed.

Widget Roles

<!-- Custom button -->
<div
role="button"
tabindex="0"
onkeydown="if(event.key==='Enter') this.click()"
>
Click Me
</div>

<!-- Tab interface -->
<div role="tablist" aria-label="Settings">
<button role="tab" aria-selected="true" aria-controls="panel1">
General
</button>
<button role="tab" aria-selected="false" aria-controls="panel2">
Privacy
</button>
</div>

<div role="tabpanel" id="panel1" aria-labelledby="tab1">
General settings content
</div>

Important ARIA Attributes

Labels and Descriptions

<!-- aria-label: labels element directly -->
<button aria-label="Close dialog">×</button>

<!-- aria-labelledby: references another element's text -->
<h2 id="dialog-title">Settings</h2>
<div role="dialog" aria-labelledby="dialog-title">
<!-- Dialog content -->
</div>

<!-- aria-describedby: provides additional description -->
<input type="password" aria-describedby="password-hint" />
<p id="password-hint">Must be at least 8 characters</p>

State Indicators

<!-- Expandable section -->
<button aria-expanded="false" aria-controls="menu">Menu</button>
<ul id="menu" hidden>
<li><a href="#">Option 1</a></li>
</ul>

<!-- Checkbox state -->
<div role="checkbox" aria-checked="false" tabindex="0">Accept terms</div>

<!-- Loading state -->
<button aria-busy="true" aria-disabled="true">
<span class="spinner"></span>
Loading...
</button>

Common Patterns

<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
aria-describedby="modal-desc"
>
<h2 id="modal-title">Confirm Action</h2>
<p id="modal-desc">Are you sure you want to proceed?</p>
<button>Cancel</button>
<button>Confirm</button>
</div>

Alert Messages

<!-- Polite announcement (waits for idle) -->
<div role="status" aria-live="polite">Profile saved successfully</div>

<!-- Assertive announcement (immediate) -->
<div role="alert" aria-live="assertive">
Error: Please fix the highlighted fields
</div>

<!-- Live region that tracks changes -->
<div aria-live="polite" aria-atomic="true">
<span>3 items in cart</span>
</div>

Form Validation

<form>
<label for="email">Email</label>
<input
type="email"
id="email"
aria-invalid="true"
aria-describedby="email-error"
required
/>
<span id="email-error" role="alert">
Please enter a valid email address
</span>
</form>

Common Mistakes

<!-- ❌ Redundant role on native element -->
<button role="button">Click</button>

<!-- ✅ Native element, no role needed -->
<button>Click</button>

<!-- ❌ Changing native semantics -->
<h1 role="button">Heading</h1>

<!-- ✅ Use appropriate element -->
<h1>Heading</h1>
<button>Action</button>

<!-- ❌ Non-interactive element with click handler -->
<span onclick="doSomething()">Click me</span>

<!-- ✅ Button with proper accessibility -->
<button onclick="doSomething()">Click me</button>

Testing Accessibility

// Basic keyboard trap prevention
document.addEventListener("keydown", (e) => {
if (e.key === "Tab") {
const focusable = modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];

if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
});

Key Points

  • Use semantic HTML first, ARIA second
  • Every interactive element needs keyboard access
  • Manage focus for custom widgets
  • Test with actual screen readers
  • Live regions announce dynamic changes
  • Don't override native element semantics