Why Accessibility Matters
Building accessible web applications isn't just about compliance—it's about creating experiences that work for everyone. When we build with accessibility in mind, we create better products for all users, not just those with disabilities.
In this article, I'll walk you through the patterns I use daily to build truly accessible React components.
The Foundation: Semantic HTML
Before reaching for ARIA attributes, start with semantic HTML. It's the foundation of accessible web development:
// Instead of this:
<div onClick={handleClick}>Click me</div>
// Do this:
<button onClick={handleClick}>Click me</button>
Semantic elements provide built-in accessibility features:
- Keyboard navigation comes for free
- Screen readers understand the element's purpose
- Focus management works automatically
ARIA Patterns for Custom Components
Sometimes semantic HTML isn't enough. When building custom components like dropdowns or modals, ARIA attributes become essential.
Example: Accessible Dropdown Menu
function Dropdown({ items, label }: DropdownProps) {
const [isOpen, setIsOpen] = useState(false);
const [activeIndex, setActiveIndex] = useState(-1);
const handleKeyDown = (e: KeyboardEvent) => {
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
setActiveIndex(prev => Math.min(prev + 1, items.length - 1));
break;
case 'ArrowUp':
e.preventDefault();
setActiveIndex(prev => Math.max(prev - 1, 0));
break;
case 'Escape':
setIsOpen(false);
break;
}
};
return (
<div onKeyDown={handleKeyDown}>
<button aria-haspopup="listbox" aria-expanded={isOpen} onClick={() => setIsOpen(!isOpen)}>
{label}
</button>
{isOpen && (
<ul role="listbox" aria-label={label}>
{items.map((item, index) => (
<li key={item.id} role="option" aria-selected={index === activeIndex}>
{item.label}
</li>
))}
</ul>
)}
</div>
);
}
Focus Management
One of the trickiest aspects of accessibility is managing focus. Here are some key principles:
- Trap focus in modals - Users shouldn't be able to tab outside a modal
- Return focus on close - When closing a modal, return focus to the trigger
- Skip links - Provide a way to skip repetitive navigation
Testing Accessibility
Building accessible components is only half the battle—you need to test them too:
| Tool | Purpose |
|---|---|
| axe-core | Automated accessibility testing |
| jest-axe | Jest integration for a11y tests |
| VoiceOver/NVDA | Manual screen reader testing |
| Keyboard testing | Navigate without a mouse |
Key Takeaways
- Start with semantic HTML before reaching for ARIA
- Implement keyboard navigation for all interactive elements
- Manage focus carefully in modals and dynamic content
- Test with real assistive technologies, not just automated tools
- Remember that accessibility benefits everyone