Accessible Design System Components: Building Inclusion at the Foundation
Accessible Design System Components: Building Inclusion at the Foundation
A design system is where accessibility either scales or fails. When individual teams build components from scratch, accessibility depends on each developer’s knowledge and prioritization. When a design system provides accessible components as the default, every product built on it inherits that accessibility. The design system is the highest-leverage point for inclusive design in an organization.
Why Design Systems Are the Leverage Point
Consider a simple button component. If the design system’s <Button> component enforces:
- Proper
<button>element semantics (not a styled<div>) - Minimum 44x44 CSS pixel touch target
- Visible focus indicator
- Sufficient color contrast for all variants
- Loading state with screen reader announcement
- Disabled state with proper
aria-disabledhandling
Then every team that uses the button gets all of this for free. Multiply this across dozens of components — modals, tabs, forms, navigation, data tables — and the design system becomes the accessibility engine for the entire organization.
Core Accessible Component Patterns
Button
The most fundamental component. Requirements:
- Uses
<button>element (or<a>for navigation actions) - Icon-only buttons have
aria-label - Loading buttons communicate state:
aria-busy="true"and visible spinner with screen reader text - Disabled buttons use
aria-disabled="true"rather than thedisabledattribute when the disabled reason needs to be communicated (disabled elements are not focusable, making it hard to discover why they are disabled) - All variants (primary, secondary, danger) meet WCAG contrast ratios
Form Controls
Text inputs, selects, checkboxes, radios, and textareas. See the W3C WAI-ARIA Authoring Practices for each:
- Every input has a visible label (not just placeholder text)
- Required fields use
aria-required="true"and a visible indicator - Error states use
aria-invalid="true"witharia-describedbypointing to the error message - Descriptions and hints use
aria-describedby - Custom selects follow the listbox or combobox pattern
- Form validation errors are announced to screen readers
Modal Dialog
Following the accessible modal pattern:
role="dialog"or native<dialog>elementaria-labelledbyandaria-describedby- Focus trap on open
- Focus return on close
- Escape key dismissal
- Background inertness
Tabs
Following the tab pattern:
role="tablist",role="tab",role="tabpanel"- Roving tabindex
- Arrow key navigation
aria-selectedstate management
Accordion
Following the accordion pattern:
- Button triggers inside headings
aria-expandedstatearia-controlslinking- Tab-key navigation between headers
Data Table
Following the table pattern:
- Semantic
<table>,<th>,<caption> scopeattributes on headers- Sortable columns with
aria-sort - Responsive behavior that preserves accessibility
Tooltip
Following the tooltip pattern:
role="tooltip"for text-only tooltipsaria-describedbylinking- Hover and focus activation
- Escape dismissal
Design System Documentation Standards
Component documentation should include accessibility information as a first-class section, not an afterthought appendix:
For Each Component, Document:
ARIA roles and attributes: What ARIA is applied automatically and what the consumer must configure (e.g., aria-label for icon buttons).
Keyboard interaction: A table of keyboard behaviors:
| Key | Action |
|---|---|
| Tab | Moves focus to the component |
| Enter/Space | Activates the button |
| Escape | Closes the dialog |
Screen reader announcements: What a screen reader user hears when interacting with the component. Include example announcements for major screen readers.
Do/Don’t guidance: Common misuse patterns that break accessibility:
- Do: Use the Button component for actions. Don’t: Use a styled
<div>with an onClick handler. - Do: Provide a visible label for every form field. Don’t: Use placeholder text as the label.
Testing checklist: Component-specific tests developers should run:
- Keyboard focus is visible
- Tab order is logical
- Screen reader announces role and state
- Color contrast meets WCAG AA
Enforcing Accessibility in the Component API
Design the component API to make inaccessible usage difficult:
Required Accessibility Props
// TypeScript: label is required, forcing accessible usage
interface ButtonProps {
children: React.ReactNode;
onClick: () => void;
/** Required for icon-only buttons */
'aria-label'?: string;
}
// Validation: warn if neither children text nor aria-label is provided
Accessible Defaults
- Focus indicators enabled by default (never
outline: nonein the base component) - Color contrast validated at the token level — design tokens that fail contrast ratios are flagged during design review
- Motion respects
prefers-reduced-motionby default in all animated components, following motion accessibility principles
Linting and Validation
- ESLint with
eslint-plugin-jsx-a11ycatches common issues in React - Storybook with
@storybook/addon-a11yruns axe on every component story - CI pipeline runs axe-core against component documentation pages
Design Tokens and Accessibility
Color Tokens
Define color token pairs with contrast ratios baked in:
{
"color": {
"text": {
"primary": { "value": "#1a1a1a", "contrast-on-white": "16.6:1" },
"secondary": { "value": "#595959", "contrast-on-white": "7.0:1" },
"disabled": { "value": "#999999", "contrast-on-white": "2.8:1", "note": "Below AA — use only with additional indicators" }
}
}
}
Document which token pairs meet which WCAG levels. Flag combinations that fail and provide guidance on when they can and cannot be used.
Spacing Tokens
Define minimum touch target sizes as tokens:
{
"spacing": {
"touch-target-minimum": { "value": "44px", "note": "WCAG 2.5.8 Target Size minimum" }
}
}
Typography Tokens
- Minimum body text size: 16px (prevents mobile browser zoom on focus)
- Line height minimum: 1.5 for body text (WCAG 1.4.12)
- Paragraph spacing: at least 2x the font size (WCAG 1.4.12)
Component Testing Strategy
Automated Testing
Run axe-core on every component variant in every state:
- Default, hover, focus, active, disabled, error, loading
- With and without optional props (labels, descriptions)
- At different viewport sizes
Unit Tests for Keyboard
Write unit tests for keyboard interaction:
it('opens dropdown on Enter key', () => {
render(<Select options={options} />);
const trigger = screen.getByRole('combobox');
fireEvent.keyDown(trigger, { key: 'Enter' });
expect(screen.getByRole('listbox')).toBeVisible();
});
Screen Reader Testing Matrix
Test critical components across:
- NVDA + Chrome (Windows)
- JAWS + Edge (Windows)
- VoiceOver + Safari (macOS)
- VoiceOver + Safari (iOS)
- TalkBack + Chrome (Android)
Document results in a compatibility matrix and address inconsistencies.
Contribution Guidelines
When accepting new components or modifications:
- Accessibility review is required — no component merges without passing automated checks and manual keyboard/screen reader testing
- ARIA pattern reference — new interactive components must reference the corresponding W3C WAI-ARIA Authoring Practices pattern
- Documentation requirement — accessibility section in component docs is mandatory, not optional
- Testing evidence — PR must include evidence of testing with at least one screen reader
Key Takeaways
A design system is where accessibility scales. By building ARIA patterns, keyboard interactions, contrast ratios, and focus management into shared components, every product inherits accessibility by default. Document the keyboard model and screen reader behavior for every component. Enforce accessibility through API design, linting, and review requirements. Test across the screen reader matrix. When the design system is accessible, the organization’s products are accessible — this is the most efficient path to inclusive digital experiences.
Sources
- W3C WAI-ARIA Authoring Practices Guide — The reference for accessible widget patterns used in design systems.
- WCAG 2.2 Specification — The standard every design system component must meet.
- Deque University: ARIA Widget Library — Accessible component implementation reference.
- The A11Y Project: Accessibility Checklist — Checklist for component-level accessibility review.