Accessibility
Last updated on 2026-03-26
The Kanban PM Kit is built with WCAG AA accessibility as a baseline. Every component and page meets or exceeds these standards, using semantic HTML, ARIA attributes, and accessible interaction patterns throughout all 45+ screens.
Skip Link
Every page includes a skip-to-content link as the first focusable element:
import { SkipLink } from "@/components/layout/skip-link"
<SkipLink />
The skip link is visually hidden until focused. It jumps the user directly to the #main-content landmark, bypassing the workspace sidebar and header navigation.
Color Contrast
All text meets WCAG AA contrast ratios:
- Normal text (< 18px): minimum 4.5:1 contrast ratio
- Large text (>= 18px bold or >= 24px): minimum 3:1 contrast ratio
- UI components and states: minimum 3:1 against adjacent colors
The oklch color token system ensures contrast is maintained in both light and dark mode. The blue-violet theme (hue 255) was specifically tuned so that primary-on-background and foreground-on-muted combinations pass AA thresholds.
Keyboard Navigation
All interactive elements are keyboard accessible:
- Tab -- navigate between focusable elements in logical order
- Shift+Tab -- navigate backwards
- Enter/Space -- activate buttons, links, and toggles
- Arrow keys -- navigate within menus, radio groups, tabs, and Kanban columns
- Escape -- close dialogs, sheets, popovers, and the command palette
Focus Indicators
Visible focus rings appear on all interactive elements using the --ring design token:
:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 2px;
}
Focus Traps
Focus is trapped inside overlay components to prevent keyboard users from tabbing behind them:
- Dialog -- task delete confirmation, project archive dialog, cycle create dialog
- Sheet -- mobile sidebar navigation
- Command -- global search command palette (Cmd+K)
When these components close, focus returns to the trigger element that opened them.
Screen Reader Support
Semantic HTML
All pages use proper HTML5 landmarks:
<header>for the app header<nav>witharia-labelfor sidebar navigation, project sub-navigation, and view switcher tabs<main>for primary content area<aside>for the sidebar and task detail panels<section>witharia-labelfor dashboard widgets, analytics sections, and settings groups<time>withdatetimeattribute for activity timestamps, due dates, and cycle date ranges
ARIA Labels
Icon-only buttons include descriptive labels:
<Button aria-label="Search tasks">
<Search className="size-4" />
</Button>
<Button aria-label="Switch to dark mode">
<Moon className="size-5" />
</Button>
Live Regions
The LiveRegion component announces dynamic content changes to screen readers:
import { LiveRegion } from "@/components/a11y/live-region"
<LiveRegion message="Task moved to In Progress" politeness="polite" />
Live regions are used for task status changes, drag-and-drop reorder announcements, subtask completion toggles, cycle progress updates, and toast notifications.
Visually Hidden
The VisuallyHidden component hides content visually while keeping it accessible to screen readers:
import { VisuallyHidden } from "@/components/a11y/visually-hidden"
<VisuallyHidden>3 tasks completed out of 12 total</VisuallyHidden>
Heading Hierarchy
Every page follows a strict heading hierarchy:
- One
<h1>per page (the page title) <h2>for major sections<h3>for subsections within those sections- No skipped heading levels
For example, the dashboard uses <h1> for "Dashboard", <h2> for "Project Progress", "Recent Activity", and "My Upcoming Tasks" sections, and <h3> within each section as needed.
Touch Targets
All interactive elements meet the 44x44px minimum touch target size. The kit includes a .touch-target utility class:
.touch-target {
min-width: 44px;
min-height: 44px;
}
This class is applied to icon buttons in the app header (search, notifications, user menu), sidebar navigation items, Kanban card actions, view switcher buttons, and priority/status selectors on mobile.
Reduced Motion
The kit respects the prefers-reduced-motion media query. When enabled:
@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 disables all animations including Kanban drag-and-drop transitions, chart entrance animations, sidebar collapse/expand transitions, notification slide-ins, shimmer loading states, stagger cascade effects, hover transforms on task and project cards, and the button press micro-interaction.
Status Indicators
Status information is never conveyed by color alone. The kit uses color plus icon plus text:
{/* Task priority uses icon + text + color */}
<ArrowUp className="size-3 text-orange-500" />
<span className="text-orange-500">High</span>
{/* Overdue task uses icon + text + color */}
<AlertCircle className="size-3 text-destructive" />
<span className="text-destructive">Overdue</span>
{/* Goal status uses icon + text + color */}
<TrendingUp className="size-3 text-chart-3" />
<span className="text-chart-3">On Track</span>
The TaskStatus, TaskPriority, LabelBadge, and GoalCard components combine color-coded indicators with readable text labels (Backlog, Todo, In Progress, In Review, Done, Cancelled) so no information depends solely on color.
Form Accessibility
Forms across the kit follow accessibility best practices:
- Labels associated with inputs via
htmlFor/id - Error messages linked with
aria-describedby - Required fields marked with visual indicators and
aria-required - Invalid fields marked with
aria-invalidfor screen reader announcement - The task create/edit form, project create form, cycle create form, goal create form, and all settings forms follow these patterns
- Zod validation errors are announced to screen readers when forms are submitted
Kanban Board Keyboard Accessibility
The Kanban board built with @hello-pangea/dnd supports full keyboard interaction:
- Space -- lift and drop a task card
- Arrow keys -- move a lifted card between columns (left/right) or reorder within a column (up/down)
- Escape -- cancel a drag operation and return the card to its original position
Each Kanban column includes an aria-label describing the status name and task count. Screen readers announce when a card is picked up, moved to a new status column, or dropped into position.
Testing Recommendations
| Tool | Purpose |
|---|---|
| axe DevTools | Automated accessibility scanning |
| VoiceOver (macOS) | Screen reader testing |
| NVDA (Windows) | Screen reader testing |
| Keyboard-only navigation | Tab through every page |
| Lighthouse | Accessibility audit score |