Usage Guide

Accessibility

TypeScript UI components are built with accessibility in mind, following WCAG 2.1 guidelines and implementing proper ARIA attributes for assistive technologies.


Focus Management

Visible Focus States

All interactive elements include visible focus indicators:

// Button focus styles
className="focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-500"

These styles use focus-visible to show focus rings only for keyboard navigation, not mouse clicks.

When building page layouts, include skip links for keyboard users:

<a
  href="#main-content"
  className="sr-only focus:not-sr-only focus:absolute focus:z-50 focus:p-4"
>
  Skip to main content
</a>

ARIA Attributes

Toggle Components

The PricingTiered billing toggle uses proper ARIA roles:

<button
  role="switch"
  aria-checked={isYearly}
  onClick={() => setIsYearly(!isYearly)}
>
  {/* Toggle UI */}
</button>

Dismissible Elements

The CTABanner component includes accessible dismiss buttons:

<button
  type="button"
  onClick={handleDismiss}
  className="..."
>
  <span className="sr-only">Dismiss</span>
  <XIcon aria-hidden="true" />
</button>

Screen Reader Text

Components use sr-only for screen-reader-only content:

// Hidden text for context
<span className="sr-only">Feature</span>

// Visible to screen readers only
<th className="sr-only">Feature name</th>

Semantic HTML

Proper Heading Hierarchy

Feature and pricing sections use semantic headings:

// Section title
<h2>Feature Centered Title</h2>

// Card titles
<h3>Individual Feature</h3>

Lists for Features

Feature lists use proper <ul> and <li> elements:

<ul className="space-y-3">
  {features.map((feature) => (
    <li key={feature.text}>
      {/* Feature content */}
    </li>
  ))}
</ul>

Tables for Comparisons

PricingComparison uses proper table structure:

<table>
  <thead>
    <tr>
      <th scope="col">Feature</th>
      <th scope="col">Free</th>
      <th scope="col">Pro</th>
    </tr>
  </thead>
  <tbody>
    {/* Table rows */}
  </tbody>
</table>

Keyboard Navigation

Interactive Elements

All buttons and links are keyboard accessible:

  • Tab to navigate between elements
  • Enter or Space to activate buttons
  • Escape to dismiss modals/banners (where applicable)

Form Controls

The newsletter signup form uses proper form structure:

<form onSubmit={handleSubmit}>
  <input
    type="email"
    required
    aria-label="Email address"
    placeholder="Enter your email"
  />
  <button type="submit">Subscribe</button>
</form>

Color Contrast

Text Contrast

Components maintain WCAG AA contrast ratios:

Text TypeLight ModeDark Mode
Headingstext-slate-900text-white
Bodytext-slate-600text-slate-400
Mutedtext-slate-500text-slate-500

Interactive States

Buttons and links have sufficient contrast in all states:

// Primary button
"bg-sky-500 text-white hover:bg-sky-600"

// Light mode contrast: 4.5:1+
// Dark mode contrast: 4.5:1+

Images and Icons

Alt Text

Image components require alt attributes:

<FeatureWithImage
  imageSrc="/feature.jpg"
  imageAlt="Developer working on code" // Required
  // ...
/>

Decorative Icons

Icons that are purely decorative use aria-hidden:

<svg aria-hidden="true" className="h-5 w-5">
  {/* Icon path */}
</svg>

Meaningful Icons

Icons that convey meaning include accessible text:

<button>
  <span className="sr-only">Close</span>
  <XIcon aria-hidden="true" />
</button>

Testing Accessibility

  • axe DevTools - Browser extension for automated testing
  • WAVE - Web accessibility evaluation tool
  • Lighthouse - Built into Chrome DevTools

Manual Testing

  1. Navigate using only keyboard (Tab, Enter, Escape)
  2. Test with screen reader (VoiceOver, NVDA, JAWS)
  3. Verify color contrast with disabled colors
  4. Test at 200% zoom level

Best Practices

Required Alt Text

Always provide meaningful alt text for images. Empty alt (alt="") is only appropriate for purely decorative images.

Motion Preferences

Respect prefers-reduced-motion for animations:

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

Don't Remove Outlines

Never use outline: none without providing an alternative focus indicator. This breaks keyboard navigation.

Previous
Component Props