Back to Blog
X

Accessibility in React: Beyond aria-label

In this article

Adding aria-label everywhere does not make an interface accessible. In compliance-driven projects, what helps most is semantic HTML, keyboard navigation, and clear feedback for screen readers.

I’ve worked on LGPD dashboards, e-commerce, and enterprise portals. These practices matter most for screen reader and keyboard-only users.


1. Semantic HTML before ARIA

The WAI-ARIA rule still holds: if a native element exists, use it.

tsx
// ❌ div pretending to be a button
<div role="button" tabIndex={0} onClick={handleClick}>Save</div>

// ✅ real button
<button type="button" onClick={handleClick}>Save</button>

Use <nav>, <main>, <article>, <button>, and headings in order (h1 → h2). Screen readers navigate by landmarks — generic divs break that flow.


2. Visible focus and tab order

Every interactive element needs clear :focus-visible. Do not remove outline without a replacement:

css
button:focus-visible {
  outline: 3px solid rgba(124, 58, 237, 0.55);
  outline-offset: 4px;
}

In modals and menus, trap focus inside the panel and return focus to the opener on close. Test with Tab and Shift+Tab only.


3. Accordions and expandable panels

For FAQ, filters, and menus, the right pattern is:

  • aria-expanded on the trigger button
  • aria-controls pointing to the panel id
  • role="region" on content with aria-labelledby
tsx
<button
  aria-expanded={isOpen}
  aria-controls="faq-panel-1"
  id="faq-trigger-1"
>
  Question
</button>
<div id="faq-panel-1" role="region" aria-labelledby="faq-trigger-1">
  Answer
</div>

Avoid display: none without thinking about announcements — content must be accessible when open.


4. Live regions for dynamic updates

Search, filters, and toasts should announce what changed:

tsx
<div aria-live="polite" aria-atomic="true" className="visually-hidden">
  {resultCount === 0 ? 'No results found' : `${resultCount} results`}
</div>

Use polite for normal updates and assertive only for critical errors.


5. Test with tools and keyboard

Automate basics with axe-core or eslint-plugin-jsx-a11y, but validate manually:

  • Keyboard-only navigation
  • VoiceOver (macOS) or NVDA (Windows)
  • Contrast with Lighthouse or DevTools

A well-written aria-label does not fix low contrast or skipped headings.


Conclusion

Accessibility in React is product work: correct HTML, predictable focus, announced states, and continuous testing.

Start with native HTML, prioritize keyboard use, and test with real people when possible. Accessible interfaces tend to be more robust for everyone.

Glauber Ariel Magalhães

Written by Glauber Ariel Magalhães

Senior software engineer. I write about React, performance, accessibility, and security — with examples from real projects.

Comments

Sign in with your GitHub account to comment. Comments are public, stored as Discussions on the blog's comments repository.