Accessibility
All landing components follow WCAG 2.1 AA standards for accessibility.
Keyboard Navigation
Navbar
- All links and buttons are keyboard accessible (Tab/Shift+Tab)
- Focus indicators visible on all interactive elements
- Mobile menu can be opened/closed with Enter/Space
- Skip to content link for screen readers
FAQ
- Accordion items can be opened/closed with Enter/Space
- Arrow keys navigate between accordion items
- Focus moves to opened content
Pricing
- Annual/monthly toggle is keyboard accessible
- All CTA buttons can be activated with Enter/Space
- Focus order follows visual layout
Screen Reader Support
Semantic HTML
All components use semantic HTML elements:
<nav> // Navigation
<section> // Content sections
<article> // Self-contained content
<footer> // Footer content
<main> // Main content area
Heading Hierarchy
Proper heading order (h1 -> h2 -> h3):
<h1>Main Page Title</h1> // One per page
<h2>Section Title</h2> // Feature, Pricing, FAQ
<h3>Subsection</h3> // Individual features
ARIA Labels
For icon-only buttons:
<button aria-label="Toggle theme">
<SunIcon />
</button>
<button aria-label="Close menu">
<XMarkIcon />
</button>
Alt Text
For all images:
<Image src="/logo.png" alt="Company logo" />
<Image src="/hero.png" alt="Dashboard preview showing analytics" />
Testimonials
Author information and ratings properly labeled:
<div aria-label={`${rating} out of 5 stars`}>
{/* star icons */}
</div>
Forms
Labels associated with inputs:
<label htmlFor="email">Email address</label>
<input id="email" type="email" aria-required="true" />
Color Contrast
All text meets WCAG AA contrast ratios:
Light Mode
| Element | Class | Ratio |
|---|---|---|
| Body text | text-zinc-900 on bg-white |
21:1 |
| Secondary text | text-zinc-600 on bg-white |
7:1 |
Dark Mode
| Element | Class | Ratio |
|---|---|---|
| Body text | text-white on bg-zinc-950 |
19:1 |
| Secondary text | text-zinc-400 on bg-zinc-950 |
8.5:1 |
Interactive Elements
- Links: Underlined + color for non-color-blind users
- Buttons: Sufficient contrast + hover states
- Focus indicators: High contrast rings
className="focus:ring-2 focus:ring-offset-2 focus:ring-zinc-900"
Motion & Animation
Respects User Preferences
@media (prefers-reduced-motion: reduce) {
.scroll-fade-in {
transition: none;
opacity: 1;
transform: none;
}
}
Best Practices
- Animations are decorative, not functional
- No flashing content (seizure risk)
- Users can access all content without animations
Touch Targets
All interactive elements meet minimum size requirements (44x44px):
<button className="p-4 min-h-[44px] min-w-[44px]">
<Icon />
</button>
Language & RTL
Language Attributes
<html lang="en">
RTL Support
Layout uses logical properties where possible for RTL support.
Testing Checklist
Manual Testing
- Tab through entire page (keyboard only)
- Test with screen reader (NVDA, JAWS, VoiceOver)
- Verify color contrast with browser DevTools
- Test with browser zoom at 200%
- Check mobile touch targets
- Test with motion preferences disabled
Common Issues
- Images have alt text
- Buttons have accessible names
- Form inputs have labels
- Headings follow logical order
- Focus indicators are visible
- Color isn't sole indicator of meaning
Automated Tools
# Install axe DevTools browser extension
# Or use automated testing
npm install --save-dev @axe-core/react
Implementation Examples
Skip Link
<a
href="#main-content"
className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 bg-white px-4 py-2 z-50"
>
Skip to main content
</a>
Screen Reader Only Text
<span className="sr-only">Opens in new tab</span>
Focus Trap for Modals
import { FocusTrap } from '@radix-ui/react-focus-scope';
<FocusTrap>
<div role="dialog" aria-modal="true">
{/* Modal content */}
</div>
</FocusTrap>