Skip to main content

ARIA Roles

Landmark roles, widget roles, and when to use ARIA vs semantic HTML.

The First Rule of ARIA

Don't use ARIA. Use semantic HTML first. Custom ARIA implementations require 5–10× more maintenance than native elements and have worse cross-browser support.

<!-- ❌ Avoid: div with ARIA role -->
<div role="button" tabindex="0" onclick="handleClick()">Click me</div>

<!-- ✅ Prefer: native button -->
<button onclick="handleClick()">Click me</button>

Only use ARIA when semantic HTML genuinely cannot achieve the required functionality.

Landmark Roles

Landmark roles create navigational structure, allowing screen reader users to skip directly to major page sections.

HTML ElementARIA RolePurpose
<header>bannerSite-wide header (top-level only)
<nav>navigationNavigation menus
<main>mainPrimary content (one per page)
<aside>complementarySidebars, supplementary content
<footer>contentinfoSite-wide footer (top-level only)
<section>regionRequires aria-labelledby or aria-label
<article>articleSelf-contained, independently distributable

When multiple landmarks of the same type exist, distinguish them with labels:

<nav aria-label="Primary navigation">...</nav>
<nav aria-label="Footer navigation">...</nav>

Widget Roles

Widget roles define interactive component behavior for custom UI that native HTML can't express:

<!-- Tab interface -->
<div role="tablist" aria-label="Settings">
<button role="tab" aria-selected="true" aria-controls="panel-general">General</button>
<button role="tab" aria-selected="false" aria-controls="panel-privacy">Privacy</button>
</div>
<div role="tabpanel" id="panel-general" aria-labelledby="tab-general">...</div>

<!-- Custom select/listbox -->
<div role="listbox" aria-label="Choose a color">
<div role="option" aria-selected="true">Blue</div>
<div role="option" aria-selected="false">Red</div>
</div>

<!-- Dialog -->
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<h2 id="dialog-title">Confirm Delete</h2>
...
</div>

States and Properties

ARIA properties are mostly static (define relationships), while states change during user interaction.

<!-- Properties (static relationships) -->
<button aria-label="Close dialog">×</button>
<input aria-describedby="hint-id">

<!-- States (dynamic conditions) -->
<button aria-expanded="false" aria-controls="menu">Menu</button>
<input aria-invalid="true" aria-required="true">
<button aria-pressed="false">Mute</button>

Always keep states synchronized with visual changes.

Real-World Impact

An e-commerce date picker case study showed screen reader users achieving 95% task success after proper ARIA implementation, up from near-zero baseline. The key was using role="dialog" with proper focus management and aria-selected on calendar date cells.

When to Use ARIA (Decision Tree)

Is there a native HTML element for this?
├── YES → Use native HTML
└── NO → Is the behavior achievable with existing elements + CSS?
├── YES → Use native HTML + CSS
└── NO → Use ARIA roles + keyboard support + state management

Content from Frontend-Master-Prep-Series08-accessibility