Ensure your UI component meets accessibility standards
Create inclusive UI components that work for all users by following accessibility best practices. Poor accessibility can prevent users from navigating your application with assistive technologies like screen readers or keyboard-only input.
This tutorial explains how to meet accessibility standards when building custom components and provides links to relevant standards.
Follow WAI-ARIA and WCAG guidelines
To meet accessibility goals, follow the Web Content Accessibility Guidelines (WCAG) 2.1 and use WAI-ARIA attributes when native semantics are not enough.
Focus on the following key requirements:
- Ensure that all interactive elements are keyboard-accessible.
- Use semantic HTML where possible (
<button>,<input>,<label>). - When semantics must be overridden, use
role,aria-*, and proper keyboard handling. - Provide clear labels with
aria-label,aria-labelledby, or visible text. - Respect color contrast, focus indicators, and skip navigation patterns (see WCAG Success Criterion 1.4.3).
For full compliance, see the WAI-ARIA Authoring Practices Guide.
Avoid div-based buttons
Never build custom buttons using only <div> elements. These elements lack semantic meaning and keyboard interactivity by default.
Incorrect:
// Not accessible – not keyboard-focusable or semantically correct
const CustomButton = () => {
return (
<div onClick={() => console.log('clicked')} style={{ padding: '8px', background: '#007bff', color: 'white' }}>
Submit
</div>
);
};This example violates multiple WCAG principles:
- No role declared
- No focus handling for keyboard users
- No visible focus indicator
- Screen readers won’t recognize it as a button
Implement an accessible custom button
Correct:
import React from 'react';
const AccessibleButton = ({ label, onClick }: { label: string; onClick: () => void }) => {
return (
<div
role="button"
tabIndex={0}
onClick={onClick}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
onClick();
}
}}
aria-label={label}
style={{
padding: '8px 16px',
backgroundColor: '#007bff',
color: 'white',
borderRadius: '4px',
cursor: 'pointer',
outline: 'none',
}}
onFocus={(e) => (e.currentTarget.style.outline = '2px solid #0056b3')}
onBlur={(e) => (e.currentTarget.style.outline = 'none')}
>
{label}
</div>
);
};This example includes:
role="button": Informs assistive tech of its purposetabIndex={0}: Allows keyboard focusonKeyDown: Enables keyboard interaction with Enter and Spacearia-label: Provides a programmatic label- Focus styling for visibility
Prefer semantic HTML when possible
Always use native elements like <button> when functionality aligns with semantics.
const NativeButton = ({ label, onClick }) => {
return <button onClick={onClick}>{label}</button>;
};The native element provides:
- Full keyboard support
- Automatic ARIA roles
- Built-in focus management
- Better compatibility across devices
Use ARIA only when native HTML does not support the desired functionality.
Validate accessibility
Use automated and manual tools to evaluate your components:
Run these tools while testing your UI to catch common accessibility issues.
Best practices
To build accessible components:
- Use semantic HTML whenever possible
- Add ARIA roles and labels only when needed
- Enable full keyboard interactivity
- Test using screen readers and accessibility tools
- Read and follow WCAG 2.1 and WAI-ARIA
By applying these standards in your custom components, you ensure all users can navigate, understand, and interact with your application.