Accessibility
Last updated on 2026-08-27
The Email Marketing Kit is built with accessibility as a first-class concern. Every screen, component, and interaction pattern meets WCAG 2.1 AA standards.
WCAG AA Color Contrast
All text and interactive elements meet the minimum contrast ratios required by WCAG AA:
| Element Type | Minimum Ratio | Verification |
|---|---|---|
| Normal text (< 18px) | 4.5:1 | All --foreground / --background pairs tested |
| Large text (>= 18px bold) | 3:1 | Heading font sizes meet ratio |
| UI components (borders, icons) | 3:1 | Status badges, form controls verified |
| Focus indicators | 3:1 | --ring against all backgrounds |
Status Badge Contrast
Status indicators never rely on color alone. Every status badge combines:
- Color -- distinct background/foreground color pair
- Text label -- human-readable status text (e.g., "Active", "Bounced")
- Icon -- contextual icon where appropriate
| Status | Color Token | Text | Accessible |
|---|---|---|---|
| Active / Sent | --success |
"Active" / "Sent" | Color + text |
| Scheduled / Sending | --warning |
"Scheduled" / "Sending" | Color + text |
| Bounced / Failed | --destructive |
"Bounced" / "Failed" | Color + text |
| Draft | --info |
"Draft" | Color + text |
Keyboard Navigation
Every interactive element in the kit is fully operable via keyboard.
Global Keyboard Patterns
| Key | Action |
|---|---|
Tab |
Move focus to the next interactive element |
Shift + Tab |
Move focus to the previous interactive element |
Enter / Space |
Activate the focused element |
Escape |
Close dialogs, sheets, and popovers |
Arrow keys |
Navigate within menus, radio groups, and tabs |
Component-Specific Patterns
| Component | Keys | Behavior |
|---|---|---|
| Sidebar navigation | Tab, Enter |
Navigate and activate menu items |
| Data tables | Tab through rows |
Focus moves through interactive cells |
| Dropdown menus | Arrow Up/Down, Enter |
Navigate and select menu items |
| Select dropdowns | Arrow Up/Down, Enter |
Navigate and select options |
| Tabs | Arrow Left/Right |
Switch between tab panels |
| Dialogs | Tab (trapped), Escape |
Focus trapped within dialog; Escape closes |
| Sheet drawers | Tab (trapped), Escape |
Focus trapped within sheet; Escape closes |
| Toggle switches | Space |
Toggle state on/off |
| Checkboxes | Space |
Toggle checked state |
| Sliders | Arrow Left/Right |
Decrease/increase value |
Email Editor Keyboard Support
The block-based email editor supports keyboard operation:
Tabto move between blocksEnterto select/edit a blockEscapeto deselect and return to the block list- Block settings are accessible via the Sheet drawer (keyboard operable)
Automation Canvas Keyboard Support
The flow builder canvas supports keyboard navigation:
Tabto move between nodesEnterto open node configuration- Node configuration panels (Sheet drawers) are fully keyboard accessible
Screen Reader Support
ARIA Landmarks
The app layout uses semantic landmarks for screen reader navigation:
<nav aria-label="Main navigation"> <!-- Sidebar -->
<main id="main-content"> <!-- Page content -->
<header> <!-- Top header bar -->
ARIA Live Regions
Dynamic content updates are announced via ARIA live regions:
- Campaign status changes -- announced when a campaign status is updated
- Automation triggers -- announced when an automation is activated or paused
- Toast notifications -- Sonner toasts use
role="status"for polite announcements - Form validation -- error messages are linked to inputs via
aria-describedby
Live Region Component
The kit includes a dedicated LiveRegion component (components/a11y/live-region.tsx) for announcing dynamic updates:
import { LiveRegion } from "@/components/a11y/live-region"
<LiveRegion>
{statusMessage}
</LiveRegion>
Visually Hidden Component
For screen-reader-only content, use the VisuallyHidden component (components/a11y/visually-hidden.tsx):
import { VisuallyHidden } from "@/components/a11y/visually-hidden"
<VisuallyHidden>
Campaign sent to 28,450 recipients
</VisuallyHidden>
Data Tables
Tables use proper semantic markup:
<table>with<thead>,<tbody>, and<th>elements- Column headers have
scope="col"for clear header-cell association - Sortable columns announce their sort direction
- Empty states use descriptive messages
Focus Management
Focus Visible Styles
All interactive elements display visible focus indicators using the --ring color token:
:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 2px;
}
The focus ring color matches the primary accent (hue 115) and meets 3:1 contrast against all backgrounds.
Focus Trapping
Modal components (Dialog, Sheet, AlertDialog) trap focus within the overlay:
- Focus moves to the first focusable element when the modal opens
Tabcycles through elements within the modalEscapecloses the modal and returns focus to the trigger element- Focus is restored to the original trigger when the modal closes
Focus on Route Changes
When navigating between pages, focus is managed by Next.js App Router. The main content area receives focus, allowing screen reader users to immediately access the new page content.
Skip Navigation
A skip link is rendered on every page as the first focusable element:
<a href="#main-content" class="skip-link">
Skip to main content
</a>
The skip link is visually hidden by default and appears on focus:
.skip-link {
position: absolute;
top: -100%;
}
.skip-link:focus {
top: 1rem;
}
Reduced Motion
All animations and transitions respect the prefers-reduced-motion user preference:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
This applies to:
- Page entrance animations (fade-in-up, scale-in)
- Chart transitions and data updates
- Canvas animations in the automation flow builder
- Shimmer loading effects
- Stagger animation delays
- Button press micro-interactions
Semantic HTML
The kit uses semantic HTML elements throughout:
| Element | Usage |
|---|---|
<nav> |
Sidebar navigation, breadcrumbs |
<main> |
Primary page content |
<header> |
Top header bar |
<section> |
Distinct page sections |
<article> |
Self-contained content blocks |
<table> |
Data tables with proper <thead> / <tbody> |
<form> |
Form containers with associated labels |
<button> |
All interactive triggers (never <div onClick>) |
<h1> - <h6> |
One <h1> per page; proper heading hierarchy |
Touch Targets
All interactive elements meet the 44px minimum touch target size recommended by WCAG:
.touch-target {
min-width: 44px;
min-height: 44px;
}
This applies to:
- Buttons and button-like elements
- Sidebar navigation items
- Table action triggers
- Toggle switches and checkboxes
- Dropdown menu items
- Tab triggers
Form Accessibility
Forms throughout the kit follow accessible patterns:
- Every input has an associated
<Label>element - Error messages are linked via
aria-describedby - Required fields are indicated visually and with
aria-required - Form submission feedback is announced via live regions
- Disabled states use
aria-disabledin addition to thedisabledattribute
Testing Recommendations
When customizing the kit, verify accessibility with:
| Tool | Purpose |
|---|---|
| axe DevTools | Automated accessibility scanning |
| Lighthouse | Performance and accessibility audit |
| VoiceOver (macOS) | Screen reader testing |
| NVDA (Windows) | Screen reader testing |
| Keyboard only | Navigate without a mouse |
| Chrome DevTools contrast checker | Verify color contrast ratios |
Related Docs
- Design Tokens -- color contrast and focus tokens
- Components -- accessible component patterns
- Customization -- maintaining accessibility when customizing