Accessibility

Last updated on 2026-05-31

The Help Desk 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.

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 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 violet theme (hue 280) was specifically tuned so that primary-on-background and foreground-on-muted combinations pass AA thresholds.

Priority and Status Colors

Priority badges (urgent, high, medium, low) and status badges (new, in-progress, pending, on-hold, resolved, closed) all use color combinations that meet AA contrast requirements. Each badge combines color with text labels so no information depends solely on color.

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 select dropdowns
  • Escape -- close dialogs, sheets, popovers, dropdown menus, 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 -- invite member dialog, ticket merge confirmation, template preview
  • Sheet -- mobile sidebar navigation, chat transcript viewer
  • DropdownMenu -- bulk actions on tickets, user menu, canned response selector
  • Command -- command palette search

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> with aria-label for sidebar navigation and tab navigation
  • <main> for primary content area
  • <aside> for the sidebar and context panels
  • <section> with aria-label for dashboard widgets, chart sections, ticket panels, and settings groups
  • <time> with datetime attribute for timestamps throughout activity feeds, ticket conversations, chat messages, and audit logs

ARIA Labels

Icon-only buttons include descriptive labels:

<Button aria-label="Search tickets">
  <Search className="size-4" />
</Button>

<Button aria-label="Switch to dark mode">
  <Moon className="size-5" />
</Button>

<Button aria-label="Close ticket">
  <X className="size-4" />
</Button>

Priority indicators include screen reader context:

<PriorityBadge priority="urgent" />
{/* Renders with aria-label="Priority: Urgent" */}

SLA timers announce remaining time:

<SlaTimer deadline="2024-01-15T14:00:00Z" />
{/* Includes aria-label with human-readable time remaining */}

Live Regions

The LiveRegion component announces dynamic content changes to screen readers:

import { LiveRegion } from "@/components/a11y/live-region"

<LiveRegion message="Ticket assigned to Sarah Miller" politeness="polite" />

Live regions are used alongside sonner toast notifications for ticket status changes, SLA breach alerts, chat message arrivals, automation rule toggles, and form submissions.

Visually Hidden Text

The VisuallyHidden component provides text for screen readers without affecting visual layout:

import { VisuallyHidden } from "@/components/a11y/visually-hidden"

<VisuallyHidden>SLA compliance rate: 94.2%</VisuallyHidden>

Heading Hierarchy

Every page follows a strict heading hierarchy:

  • One <h1> per page (the page title, rendered by PageHeader)
  • <h2> for major sections (card titles, section headings)
  • <h3> for subsections within those sections
  • No skipped heading levels

For example, the agent dashboard uses <h1> for "Dashboard", and <h2> for "My Queue", "Activity Feed", "Quick Stats", and "Recent Customers" sections.

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 (theme toggle, notifications, user menu), sidebar navigation items, ticket action buttons, chat message actions, kanban card controls, and pagination controls 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 chart entrance animations, card fade-in-up transitions, sidebar collapse/expand transitions, shimmer loading states, kanban drag animations, chat typing indicators, floating decorative elements, stagger delay sequences, and button press micro-interactions.

The use-reduced-motion hook is also available for programmatic checks:

import { useReducedMotion } from "@/hooks/use-reduced-motion"

const prefersReducedMotion = useReducedMotion()

Status Indicators

Status information is never conveyed by color alone. The kit uses color plus icon plus text:

{/* Priority uses badge + text + color */}
<PriorityBadge priority="urgent" />
{/* Renders: red badge with "Urgent" text */}

{/* SLA status uses icon + text + color */}
<SlaTimer status="at-risk" />
{/* Renders: yellow timer icon with countdown text */}

{/* Agent status uses dot + text + color */}
<AgentStatusIndicator status="available" />
{/* Renders: green dot with "Available" text */}

The StatusBadge, PriorityBadge, SlaTimer, and AgentStatusIndicator components all combine color-coded visuals with readable text labels so no information depends solely on color.

Form Accessibility

Forms across the kit follow accessibility best practices:

  • Labels associated with inputs via htmlFor/id pairing
  • Error messages linked with aria-describedby
  • Required fields marked with visual indicators and aria-required
  • Invalid fields marked with aria-invalid for screen reader announcement
  • The ticket creation form, settings forms, article editor, canned response editor, automation rule builder, and filter controls all follow these patterns
  • Toast notifications from sonner announce form submission results

Table Accessibility

Data tables throughout the kit use semantic <table>, <thead>, <tbody>, <th>, and <td> elements:

  • Column headers use <TableHead> with proper scope
  • Clickable rows include cursor-pointer styling for visual affordance
  • Responsive columns use hidden md:table-cell to progressively show content on larger screens while maintaining table semantics
  • Monospace formatting on ticket IDs and numeric values uses tabular-nums for consistent alignment
  • Sortable columns indicate sort direction to screen readers

Accessibility Hooks

The kit includes five accessibility-focused hooks:

Hook Purpose
use-announce Programmatically announce messages to screen readers via ARIA live regions
use-focus-trap Trap focus within modal dialogs and overlay components
use-keyboard-navigation Enable keyboard navigation patterns (arrow keys, escape)
use-mobile Detect mobile viewport for responsive accessibility adjustments
use-reduced-motion Detect prefers-reduced-motion preference

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

Need a professional accessibility review? Try our free WCAG compliance checker to scan any URL for accessibility issues.