![]()
Implementing Accessibility Best Practices in JSX
Ignoring accessibility in React applications creates barriers for users with disabilities. Here’s how to properly address accessibility concerns in your JSX components:
Essential Accessibility Fixes
1. Image Alt Text (Critical for Screen Readers)
Bad:
<img src="logo.png" />
Good:
<img
src="logo.png"
alt="Company logo - return to homepage"
/>
For decorative images:
<img
src="divider.png"
alt=""
aria-hidden="true"
/>
2. Form Label Associations
Bad:
<span>Email</span>
<input type="email" name="email" />
Good:
<label htmlFor="email-input">Email</label>
<input
id="email-input"
type="email"
name="email"
aria-required="true"
/>
3. Button Accessibility
Bad:
<div onClick={handleClick}>Submit</div>
Good:
<button
type="button"
onClick={handleClick}
aria-label="Submit form"
>
Submit
</button>
Comprehensive Accessibility Practices
1. Semantic HTML Structure
<header>
<nav aria-label="Main navigation">
{/* Navigation items */}
</nav>
</header>
<main>
<article>
<h1>Page Title</h1>
{/* Content */}
</article>
</main>
<footer>
{/* Footer content */}
</footer>
2. ARIA Attributes When Needed
<div
role="alert"
aria-live="assertive"
>
{errorMessage}
</div>
<button
aria-expanded={isExpanded}
aria-controls="expandable-section"
>
Toggle Section
</button>
3. Focus Management
function Modal({ onClose }) {
const closeButtonRef = useRef();
useEffect(() => {
closeButtonRef.current.focus();
}, []);
return (
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
>
<h2 id="modal-title">Confirmation</h2>
<button
ref={closeButtonRef}
onClick={onClose}
>
Close
</button>
</div>
);
}
Tools to Enforce Accessibility
1. ESLint Plugin
// .eslintrc.js
module.exports = {
plugins: ['jsx-a11y'],
extends: [
'plugin:jsx-a11y/recommended'
]
};
2. Testing Libraries
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
test('has proper alt text', () => {
render(<img src="test.png" alt="Test image" />);
expect(screen.getByAltText('Test image')).toBeInTheDocument();
});
3. Browser Extensions
Common Accessibility Pitfalls to Avoid
- Non-descriptive links:
// Bad
<a href="/about">Click here</a>
// Good
<a href="/about">About our company</a>
- Missing heading hierarchy:
// Bad - skips heading levels
<h1>Main title</h1>
<h3>Subsection</h3>
// Good - proper hierarchy
<h1>Main title</h1>
<h2>Subsection</h2>
- Color contrast issues:
// Bad - low contrast
<div style={{ color: '#999999' }}>Low contrast text</div>
// Good - minimum 4.5:1 ratio
<div style={{ color: '#767676' }}>Accessible text</div>
Advanced Techniques
1. Skip Navigation Links
function SkipLink() {
return (
<a
href="#main-content"
className="skip-link"
>
Skip to main content
</a>
);
}
// Then at main content start:
<main id="main-content">
{/* Content */}
</main>
2. Accessible Notifications
function LiveAnnouncement() {
const [message, setMessage] = useState('');
return (
<div
aria-live="polite"
aria-atomic="true"
>
{message && <div>{message}</div>}
</div>
);
}
3. Dark Mode Considerations
function ThemeToggle() {
const [darkMode, setDarkMode] = useState(false);
return (
<button
aria-pressed={darkMode}
onClick={() => setDarkMode(!darkMode)}
>
{darkMode ? 'Disable dark mode' : 'Enable dark mode'}
</button>
);
}
