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:

  1. Color -- distinct background/foreground color pair
  2. Text label -- human-readable status text (e.g., "Active", "Bounced")
  3. 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:

  • Tab to move between blocks
  • Enter to select/edit a block
  • Escape to 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:

  • Tab to move between nodes
  • Enter to 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
  • Tab cycles through elements within the modal
  • Escape closes 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-disabled in addition to the disabled attribute

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