A specification that defines roles, states, and properties to improve accessibility of complex UI when native semantics are insufficient.
ARIA stands for Accessible Rich Internet Applications, but don't let the boring acronym fool you – ARIA is what makes modern web apps usable for everyone. It's a set of HTML attributes that add semantic meaning to elements, telling assistive technologies what things are and how they behave.
Think of ARIA as subtitles for your code. Screen readers can see your HTML structure, but without ARIA, they can't tell if that
The catch? ARIA is simultaneously incredibly powerful and dangerously easy to misuse. Use it wrong, and you'll make your site less accessible than if you'd used no ARIA at all. That's why the first rule of ARIA is: "No ARIA is better than bad ARIA."
Before you start slapping aria-* attributes everywhere, memorize these rules from the W3C:
Rule 1: Don't use ARIA if you can use native HTML. That semantic
ARIA roles tell assistive technologies what an element's purpose is. There are dozens of roles, but here are the ones you'll actually use:
Landmark Roles: Structure your page. banner, navigation, main, complementary, contentinfo, search, form. These let screen reader users jump directly to page sections. Use them, and users can skip straight to your content instead of tabbing through your entire navigation.
Widget Roles: Interactive components. button, link, textbox, checkbox, radio, slider, tab, tabpanel, dialog, menu, menuitem. Most have native HTML equivalents you should use instead. But for custom components, these are essential.
Document Structure Roles: Organize content. heading (with aria-level), list, listitem, table, row, cell. Again, use native HTML when possible, but these help when building complex layouts.
Live Region Roles: Dynamic content. alert, status, log, marquee, timer. These announce updates to screen reader users without requiring them to navigate to the content. Perfect for notifications, live chat, real-time data.
The mistake everyone makes? Using role="button" on a div instead of just using a
Roles tell what something is; states and properties tell its current condition and characteristics.
States (change frequently): - aria-expanded="true/false": Is this dropdown open? Users need to know. - aria-checked="true/false/mixed": Checkbox state. Mixed for indeterminate checkboxes. - aria-disabled="true": Like the disabled attribute but for custom controls. - aria-hidden="true": Remove from accessibility tree. Use sparingly – usually wrong. - aria-selected="true": Which tab/option is currently selected. - aria-pressed="true/false": Toggle button state. Different from checked.
Properties (more stable): - aria-label: Direct text label for an element. "Close" for your X button. - aria-labelledby: Points to another element that labels this one. More flexible. - aria-describedby: Additional description beyond the label. Help text, errors, instructions. - aria-required="true": Mark form fields as required. Visual * isn't enough. - aria-live="polite/assertive": How urgently to announce dynamic content updates. - aria-controls: What does this button control? Link tabs to their panels.
The gotcha: These don't validate themselves. Set aria-expanded but forget to toggle it when the dropdown opens? Screen readers will announce the wrong state. Users will be confused. That's worse than no ARIA at all.
Let's talk about what not to do, because everyone does these wrong at first:
Redundant ARIA:
Hidden Accessible Names:
Fake Keyboard Support:
Hiding Interactive Content: – You told screen readers to ignore a button. Why? This breaks keyboard navigation and makes the button invisible to assistive technology.
Static ARIA: const [expanded, setExpanded] = useState(false), but your JSX has aria-expanded="false" hardcoded. You built state management but forgot to connect it to ARIA. The dropdown works visually but screen readers never know it's open.
Empty Labels: aria-label="" or aria-labelledby pointing to a non-existent ID. Screen readers announce nothing. Better to have no ARIA attribute than an empty one.
Wrong Live Regions: Using aria-live="assertive" for everything. Users are trying to read your page and you keep interrupting with "Cart updated! Loading! Done! Error!" Use "polite" unless it's truly urgent.
WebAbility automatically detects these mistakes in real-time, fixing the most critical issues while alerting you to patterns that need deeper fixes. Because ARIA bugs are often invisible until a screen reader user encounters them.
Modern JavaScript frameworks make ARIA both easier and harder. Easier because you can bind ARIA attributes to state. Harder because you actually have to remember to do it.
React: JSX makes ARIA attributes first-class citizens. All aria-* attributes work directly. Use state for dynamic ARIA:
const [expanded, setExpanded] = useState(false);
return (
Pro tip: Many React UI libraries (Material-UI, Chakra, etc.) handle ARIA automatically. But verify – many claim accessibility but miss edge cases.
Vue: Similar approach with v-bind. Vue's reactivity makes ARIA state management natural:
Watch out: ARIA attributes need string values, but Vue boolean bindings give you true/false. Convert to strings.
Angular: Template syntax works well with ARIA, but you need to sanitize dynamic content:
The pattern is the same everywhere: manage ARIA state alongside visual state. When your component changes, ARIA must change too. Forget this and you've built a beautiful interface that lies to screen reader users.
ARIA is invisible to sighted users and automated tools often miss problems. Here's how to actually test:
Browser Dev Tools: Chrome and Firefox show the accessibility tree. Inspect an element and look at the Accessibility panel. Does the role match what you intended? Is the name correct? Are states reflected?
Screen Readers: The only real test. NVDA (Windows, free), VoiceOver (Mac/iOS, built-in), JAWS (expensive but common). Close your eyes and try using your interface. Can you understand what each element does? Does it announce correctly?
axe DevTools: Browser extension that catches many ARIA mistakes. Required roles, invalid attributes, missing labels. But it can't catch logic errors – aria-expanded that never changes, for example.
Lighthouse: Built into Chrome DevTools. Automated audit finds low-hanging fruit. Not comprehensive, but a good first pass.
Manual Checklist: - Can you reach every interactive element by keyboard? - Does each control have a clear name? - Do states update when the UI changes? - Are dynamic updates announced appropriately? - Can you complete your key tasks without seeing the screen?
WebAbility provides continuous monitoring, catching ARIA regressions before they reach production. We test with actual screen readers and provide specific fixes, not just "add ARIA" advice.
Here's a decision tree for ARIA usage:
Use Native HTML When:
- There's a semantic element that does what you need
-
Use ARIA When: - Building custom interactive widgets not available in HTML (tab panels, tree views, comboboxes) - Adding semantic structure to generic containers (
Join over 1 million websites using WebAbility to ensure digital accessibility compliance and provide equal access to all users.
Supplemental help text associated with an element, often via aria-describedby, that provides additional guidance beyond the accessible name.
An area of the page that notifies assistive technologies about dynamic updates. Use sparingly with polite or assertive announcements.
Text associated with an input that communicates its purpose. Implement with <label for> or ARIA labelling.
Page sections identified by semantic elements or ARIA roles (main, navigation, complementary, banner, contentinfo) for quick navigation.
This glossary is continuously improved and maintained by WebAbility to advance accessible design and development.Contact us to suggest improvements or report issues.