home / skills / sergiodxa / agent-skills / frontend-accessibility-best-practices

frontend-accessibility-best-practices skill

/skills/frontend-accessibility-best-practices

This skill helps you implement accessibility best practices in React components, improving semantic structure, keyboard support, and screen reader experience.

npx playbooks add skill sergiodxa/agent-skills --skill frontend-accessibility-best-practices

Review the files below or copy the command above to add this skill to your agents.

Files (8)
SKILL.md
4.0 KB
---
name: frontend-accessibility-best-practices
description: Accessibility (a11y) best practices for React components. Use when creating UI components, forms, interactive elements, or reviewing code for accessibility compliance.
---

# Accessibility Best Practices

Accessibility patterns for building inclusive React applications following WCAG standards. Contains 7 rules across 4 categories focused on semantic HTML, screen reader support, keyboard navigation, and user preferences.

## When to Apply

Reference these guidelines when:

- Creating new UI components
- Building forms and interactive elements
- Adding dynamic content or notifications
- Implementing navigation patterns
- Reviewing code for accessibility

## Rules Summary

### Semantic HTML & Structure (HIGH)

#### semantic-html-landmarks - @rules/semantic-html-landmarks.md

Use semantic HTML elements for page structure.

```tsx
// Bad: divs with class names
<div className="header">...</div>
<div className="nav">...</div>
<div className="content">...</div>

// Good: semantic elements
<header>...</header>
<nav aria-label={t("Primary")}>...</nav>
<main>...</main>
<footer>...</footer>
```

### Screen Readers (MEDIUM)

#### screen-reader-sr-only - @rules/screen-reader-sr-only.md

Use sr-only class for visually hidden text.

```tsx
// Icon-only buttons need accessible labels
<Button variant="icon" onPress={onClose}>
  <XMarkIcon aria-hidden="true" />
  <span className="sr-only">{t("Close")}</span>
</Button>

// Visually hidden section headings
<section>
  <h2 className="sr-only">{t("Search results")}</h2>
  <SearchResultsList />
</section>
```

#### aria-live-regions - @rules/aria-live-regions.md

Announce dynamic content changes to screen readers.

```tsx
// Error messages - announced immediately
{
  error && (
    <p role="alert" className="text-failure-600">
      {error}
    </p>
  );
}

// Status updates - announced politely
<div role="status" aria-live="polite">
  {t("{{count}} results found", { count })}
</div>;
```

### Keyboard & Focus (HIGH)

#### keyboard-navigation - @rules/keyboard-navigation.md

Use semantic elements for built-in keyboard support.

```tsx
// Bad: div with onClick not keyboard accessible
<div onClick={handleClick}>Click me</div>

// Good: button has Enter/Space support
<button onClick={handleClick}>Click me</button>

// Good: react-aria Button handles everything
import { Button } from "react-aria-components";
<Button onPress={handlePress}>Click me</Button>
```

#### focus-management - @rules/focus-management.md

Show visible focus indicators and trap focus in modals.

```tsx
// Always use focus-visible for focus styles
<button className="focus-visible:ring-2 focus-visible:ring-teal-600">
  Click me
</button>;

// react-aria Modal handles focus trapping automatically
import { Modal, Dialog } from "react-aria-components";
<Modal isOpen={isOpen}>
  <Dialog>{/* Focus automatically trapped here */}</Dialog>
</Modal>;
```

### User Preferences (MEDIUM)

#### reduced-motion - @rules/reduced-motion.md

Respect prefers-reduced-motion setting.

```tsx
import { usePrefersReducedMotion } from "~/hooks/use-prefers-reduced-motion";

// CSS approach
<div className="animate-bounce motion-reduce:animate-none">
  Bouncing content
</div>;

// JS approach
function AnimatedCounter({ value }) {
  let prefersReducedMotion = usePrefersReducedMotion();
  if (prefersReducedMotion) return <span>{value}</span>;
  return <CountUp target={value} />;
}
```

#### touch-targets - @rules/touch-targets.md

Ensure 44x44px minimum touch targets.

```tsx
// Icon buttons need explicit sizing
<Button variant="icon" className="h-11 w-11">
  <XMarkIcon className="h-5 w-5" />
  <span className="sr-only">{t("Close")}</span>
</Button>

// Links need padding for tappable area
<Link to={href} className="block py-3 px-4">
  {label}
</Link>
```

## Key Files

- `app/components/heading.tsx` - Region, Heading, Main components
- `app/hooks/use-prefers-reduced-motion.ts` - Reduced motion hook
- `app/components/field/field.tsx` - Accessible form field component

Overview

This skill provides concise accessibility (a11y) best practices for building inclusive React components following WCAG guidance. It bundles rules across semantic HTML, screen reader support, keyboard & focus behavior, and user preference handling. Use it to design, implement, or review UI components, forms, and interactive patterns for real-world apps.

How this skill works

The skill inspects common component patterns and recommends concrete fixes and examples: prefer semantic HTML landmarks, add screen-reader-only labels, use ARIA live regions for dynamic updates, ensure keyboard operability and visible focus, and honor user motion preferences. It points to practical implementations such as accessible buttons, focus-trapped modals, reduced-motion fallbacks, and appropriate touch target sizing.

When to use it

  • Creating new UI components or layout regions
  • Building or validating forms and form fields
  • Adding interactive controls, buttons, or menus
  • Implementing dynamic updates or notification content
  • Reviewing code for accessibility regressions

Best practices

  • Use semantic HTML (header, nav, main, footer, h1–h6, landmarks) instead of divs for structure and landmarks
  • Provide accessible names for icon-only controls using visually hidden text (sr-only) or aria-label
  • Announce dynamic content with role="alert" or aria-live="polite" as appropriate
  • Prefer native controls (button, a, input) for built-in keyboard support; avoid click-only divs
  • Show visible focus indicators and trap focus in modal dialogs; ensure focus order is logical
  • Respect prefers-reduced-motion and ensure touch targets are at least 44x44px

Example use cases

  • Turning a div-based header/nav into semantic <header> and <nav aria-label> landmarks
  • Making icon-only close buttons accessible with an sr-only label and aria-hidden icons
  • Wrapping status updates in a role="status" region so screen readers announce result counts
  • Replacing clickable divs with <button> or react-aria Button for keyboard activation
  • Using a reduced-motion hook to skip animations for users who prefer less motion

FAQ

When should I use aria-live versus role="alert"?

Use role="alert" for important error messages that need immediate announcement. Use aria-live="polite" for non-critical status updates like result counts to avoid interrupting the user.

How do I handle focus when opening a modal?

Set initial focus to a meaningful element inside the modal, trap focus within the dialog, and restore focus to the element that opened it. Use libraries like react-aria or a modal component that manages focus automatically when possible.