The First Rule of ARIA
The Accessible Rich Internet Applications specification was designed to fill gaps where native HTML fell short. But the modern web has evolved significantly, and HTML has become far more capable. The first and most important rule of ARIA: if a native HTML element or attribute exists, use it.
Native HTML elements have built-in semantics, keyboard functionality, and assistive technology support that's been battle-tested across browsers and screen readers. Using a native <button> element automatically gives you keyboard support (Space and Enter), correct focus management, and proper announcement as a button. A div with role="button" requires you to manually implement all of this functionality.
ARIA never changes visual appearance or behaviour. It only modifies semantic meaning for assistive technology. If you need to change how something looks or works, use HTML and CSS. ARIA supplements, not replaces.
The Five Rules of ARIA Use
The W3C outlines five fundamental rules for responsible ARIA authoring. Understanding these rules prevents common mistakes that can render ARIA harmful rather than helpful.
1. Use native HTML when it exists. A <button> is better than a div with role="button". A <nav> is better than a div with role="navigation". Native elements have proven functionality.
2. Don't change native semantics. Never write <button role="heading"> or <h2 role="button">. This creates confusion for assistive technology users and developers.
3. All interactive ARIA controls must be keyboard-operable. If you build a custom dropdown with role="combobox", it must respond to keyboard input (arrow keys, Escape, Enter) just like a native select. No mouse-only widgets.
4. Don't hide focusable elements with aria-hidden. If an element is interactive and keyboard-accessible, don't apply aria-hidden="true" to it. This creates a broken experience where the element is visible and focusable, but screen reader users can't interact with it.
5. All interactive elements must have an accessible name. Screen readers must be able to announce what a button does, what a link goes to, what a form field expects. Use labels, aria-label, or aria-labelledby to provide these names.
ARIA Labelling Techniques
When native HTML labels aren't sufficient, three ARIA properties provide alternatives: aria-labelledby, aria-describedby, and aria-label.
aria-labelledby references the ID of another element that labels the current element. It has the highest priority for accessible naming. Use it when a visible text element already exists that describes your control:
<h2 id="dialog-title">Confirm your action</h2>
<div role="dialog" aria-labelledby="dialog-title">
Are you sure you want to delete this item?
</div>
aria-describedby provides supplemental description beyond the label. Use it for hints, additional context, or error messages:
<label for="password">Password</label>
<input id="password" type="password" aria-describedby="pwd-hint" />
<div id="pwd-hint">Must be at least 8 characters, including one number.</div>
aria-label provides an inline label string when no visible text exists. Use it sparingly. Visible labels are always preferable for all users:
<button aria-label="Close dialog">×</button>
Enhancing Buttons & Controls
ARIA properties enhance button and control semantics to communicate state and behaviour to screen readers. aria-expanded indicates whether a control reveals or hides additional content. Use it on accordion headers and disclosure buttons:
<button aria-expanded="false" aria-controls="menu">Menu</button>
aria-pressed indicates toggle button state (active or inactive). Use it for "bold", "italic", "favorite" buttons that maintain a pressed state:
<button aria-pressed="false">Add to favorites</button>
aria-haspopup communicates that a button triggers a popup (menu, dialog, listbox). This primes screen reader users for what will happen:
<button aria-haspopup="menu">Actions</button>
Hiding & Showing Content
aria-hidden="true" hides an element from screen readers while keeping it visible and interactive. Use it on decorative icons paired with visible text, or elements that would create noise:
<button><span aria-hidden="true">+</span> Add item</button>
role="presentation" strips an element of its default semantics, useful for layout tables or elements serving only presentational purposes. Never apply it to elements requiring accessibility. The presence of aria-label, for example, overrides presentation.
The visually hidden class pattern keeps content visible to screen readers but hidden from sight. Use it for skip links and sr-only content:
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
}
Writing Accessible Links
WCAG 2.4.4 and 2.4.9 require that link text describe the link's destination without relying on surrounding context. Screen readers can list all links on a page, and users need to understand each link from the text alone.
Avoid generic phrases: "click here", "read more", "learn more" don't convey destination or purpose. "Click here for our latest article on accessibility" becomes "Our latest article on accessibility" as link text. Front-load key words. The Nielsen Norman Group found that users focus primarily on the first 11 characters of link text.
Repeating the same link text with different destinations ("Learn more, Learn more, Learn more") creates ambiguity. Each link should have unique, descriptive text that stands alone.
ARIA Live Regions
Live regions announce dynamic content changes to screen reader users without requiring focus to move. aria-live="polite" waits for a pause in speech before announcing updates (ideal for non-urgent messages). aria-live="assertive" interrupts immediately (reserved for errors and time-sensitive information).
role="status" (implied polite) and role="alert" (implied assertive) are common live region implementations. A form validation message might use role="alert", while a shopping cart item count uses role="status".
Common mistake: applying aria-live to elements that don't actually update. If content never changes dynamically, aria-live has no effect. Only use it on regions that update in response to user actions or real-time data.
Testing With Real Screen Readers
Testing with real screen readers is irreplaceable. Automated tools catch many issues, but human testing reveals UX problems that tools miss. Each screen reader announces content slightly differently, and testing helps identify inconsistencies.
NVDA on Windows + Chrome is the most accessible testing combination. Key shortcuts: H for headings, Ins+F7 for a list of links, Ins+F1 for current element info, Ins+Down Arrow to read the next line. The NVDA User Guide provides a comprehensive shortcut reference.
VoiceOver on macOS + Safari uses VO (Control+Option) for commands. VO+U opens the rotor, showing headings and links organized by category. Test using your keyboard rather than mouse. Screen reader users navigate entirely by keyboard.
TalkBack on Android provides mobile screen reader testing. Swipe right to navigate forward, left to navigate backward. Two-finger swipe to scroll.
When testing, verify:
- Page title is descriptive and announced first
- Heading structure is logical (no skipped levels)
- Link text is descriptive and unique
- Form labels are properly associated with inputs
- Dynamic content changes are announced
- Error messages are clearly communicated
- All interactive elements are keyboard-accessible