# Web accessibility implementation guide [2026 edition] — practical techniques to comply with WCAG 2.2 in React / Next.js

> A complete guide to accessibility implementation that complies with WCAG 2.2 (AA) in React / Next.js. With real code it explains semantic HTML, keyboard operation, focus management, the correct use of ARIA, forms, contrast, prefers-reduced-motion, the new WCAG 2.2 criteria, and automated testing with axe.

- Published: 2026-06-24
- Author: 友田 陽大
- Tags: React, Next.js, フロントエンド, アクセシビリティ, TypeScript, アーキテクチャ設計
- URL: https://tomodahinata.com/en/blog/react-nextjs-web-accessibility-wcag22-guide
- Category: Frontend
- Pillar guide: https://tomodahinata.com/en/blog/nextjs-16-app-router-cache-components-data-fetching

## Key points

- Accessibility is not handling people with disabilities but the usability of all users, and is a total score of quality strongly correlated with SEO.
- The level to aim for is WCAG 2.2 Level AA. Of the 9 criteria added in 2.2, 6 affect AA.
- 80% of a11y is solved with correct semantic HTML. ARIA is the last resort to fill the gap (No ARIA is better than bad ARIA).
- Reach all operations by keyboard, make focus visible with :focus-visible, and implement a focus trap and return for modals.
- Protect with a two-tier setup: automated inspection by axe (detects 30–50%) and manual confirmation by keyboard and screen reader.

---

Accessibility is where the "total score of quality" appears. Sloppy component design, gaps in state management, neglected focus — all surface as a11y defects. Conversely, a team that can guarantee a11y has high code quality itself.

---

## 1. Why accessibility now (the business viewpoint)

Before the technical discussion, let's organize the value for the client.

- **Expanding the target users.** People who need some support amount to a considerable fraction of the population. a11y is directly an expansion of the reachable user count.
- **Legal / procurement requirements.** In public and large-enterprise procurement, WCAG compliance is increasingly a requirement. Not complying can mean "not even being a candidate."
- **Synergy with SEO.** Proper heading structure, alt text, and semantic markup work for both search engines and screen readers. a11y improvement is also acquisition improvement.
- **Proof of quality.** An accessible product is evidence that the design is careful. It becomes a trust material for the ordering decision.

In other words, a11y is not a "cost" but an investment in "reach, compliance, SEO, and trust."

---

## 2. The foundation: WCAG 2.2's 4 principles (POUR)

WCAG all boils down to 4 principles. Keep these in mind and you can understand individual techniques by "why they're needed."

| Principle | Meaning | Representative example |
| ---- | ---- | ---- |
| Perceivable | information can be perceived | image alt text, sufficient contrast |
| Operable | the UI can be operated | keyboard operation, sufficient target size |
| Understandable | content and operation can be understood | consistent navigation, clear errors |
| Robust | assistive technology can interpret | valid HTML, proper name/role/value |

The levels are A (minimum) < AA (standard) < AAA (highest). **The practical goal is AA.**

---

## 3. The biggest lever: fully use semantic HTML

80% of a11y can be achieved just by using the correct element. Stopping "making" buttons and links with `div` and `span` is the most cost-effective improvement.

```tsx
// ❌ アンチパターン：div をボタンに見せかける
<div className="btn" onClick={handleClick}>送信</div>

// ✅ button は「フォーカス可能・Enter/Space で発火・支援技術がボタンと認識」を無料で得る
<button type="button" onClick={handleClick}>送信</button>
```

Key points:

- **Page transition is `a` (in Next.js, `Link`), an action is `button`.** Choose by meaning, not appearance.
- **Keep the heading hierarchy.** `h1` is one per page; don't skip `h2 → h3`. Screen-reader users "skim-read" the page by headings.
- **Show regions with landmarks.** Use `header` / `nav` / `main` / `footer` and assistive technology can present the page structure. `main` is one.
- **Lists are `ul`/`ol`, tables are `table` + `th` (with `scope`).** A `div` that only resembles them in appearance loses structural information.

> Semantic HTML is overwhelmingly more robust and lower in maintenance cost than "supplementing later with ARIA." This is KISS itself.

---

## 4. Keyboard operation and focus management

For users who can't use a mouse (motor disability, screen reader, power user), being able to reach all functions with the keyboard alone is a lifeline.

### 4-1. Can you reach every operation with the keyboard

- Move in a logical order with Tab, and operate with Enter/Space/arrows.
- **`tabindex` is only `0` (include in focus order) or `-1` (programmatically focusable).** A positive `tabindex` breaks the focus order, so don't use it.
- Follow the keyboard patterns of the [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/) for custom widgets (tabs, menus, comboboxes). Not building them yourself but using a verified headless UI like Radix UI is safe and fast.

### 4-2. Focus must be "visible"

```css
/* ❌ 絶対にやってはいけない：フォーカスリングを消すだけ */
:focus { outline: none; }

/* ✅ マウス利用時は控えめ、キーボード利用時ははっきり見せる */
:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}
```

### 4-3. Focus movement on route transition / modal

In an SPA / App Router, focus tends to be left behind even when the page transitions. For a modal, "focus inside on open, return to the opening element on close, and not touchable behind with Tab (focus trap)" is essential.

```tsx
function Dialog({ open, onClose, children }: DialogProps) {
  const ref = useRef<HTMLDivElement>(null);
  const opener = useRef<HTMLElement | null>(null);

  useEffect(() => {
    if (open) {
      opener.current = document.activeElement as HTMLElement;
      ref.current?.focus(); // 開いたら中へ
    } else {
      opener.current?.focus(); // 閉じたら開いた要素へ戻す
    }
  }, [open]);

  // Escape で閉じる、フォーカストラップ等は Radix Dialog 等に任せるのが堅実
  return open ? (
    <div role="dialog" aria-modal="true" tabIndex={-1} ref={ref}>
      {children}
    </div>
  ) : null;
}
```

In practice, using Radix UI's `Dialog`, which has such behavior complete, is the shortest and safest (this site's UI is also Radix-based). Reinventing the wheel tends to become a YAGNI violation.

---

## 5. ARIA is "the last resort" — the correct way to use it

ARIA is a tool to supplement dynamic information that HTML alone can't express (expanded state, live updates, etc.). Overuse is counterproductive.

ARIA's 5 iron rules (a summary of the official "Rules of ARIA"):

1. **If there's a native HTML element, use it.** `button` over `role="button"`.
2. **Don't override a native meaning with ARIA.** Don't destroy like `<h2 role="tab">`.
3. **All interactive ARIA must be keyboard-operable.**
4. **Don't hide a focusable element with `role="presentation"` / `aria-hidden="true"`.** (Don't create the contradiction of being focusable yet invisible to assistive technology)
5. **Give an accessible name to all operable elements.**

Commonly-used correct examples:

```tsx
// アイコンのみボタンには必ずラベルを
<button aria-label="メニューを開く" onClick={toggle}>
  <MenuIcon aria-hidden="true" />
</button>

// 開閉状態を伝える
<button aria-expanded={open} aria-controls="panel">詳細</button>
<div id="panel" hidden={!open}>...</div>

// 動的更新の通知（バリデーション結果やトースト）
<div role="status" aria-live="polite">{message}</div>
```

---

## 6. Form accessibility

Forms are where a11y failures are most common (for detailed implementation, see the [React Hook Form × Zod practical guide](/blog/react-hook-form)). Keep at least these.

- **A `label` for every input (associated with `htmlFor`).** A `placeholder` is not a substitute for a label (it disappears on input and has low contrast).
- **Tie errors to the input with `aria-invalid` and `aria-describedby`, and notify with `role="alert"`.**
- **Don't rely on color alone for required.** Show with text or `*` + a legend, and add `aria-required`.
- **On submit failure, focus the first error item.**

```tsx
<label htmlFor="email">メールアドレス</label>
<input
  id="email"
  type="email"
  aria-invalid={!!error}
  aria-describedby={error ? "email-error" : undefined}
/>
{error && <p id="email-error" role="alert">{error}</p>}
```

---

## 7. Color, contrast, motion

### Color and contrast

- **Body text has a contrast ratio of 4.5:1 or more with the background** (large text is 3:1). It's an AA mandatory criterion.
- **Don't convey information by color alone.** "Red = error" is used together with an icon or text (consideration for color-vision characteristics).

### Motion (prefers-reduced-motion)

For people with vestibular disorders, large animations cause physical discomfort. Respect the OS's "reduce motion" setting. **This site's animation layer is also designed to respect `prefers-reduced-motion`.**

```css
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
```

You can also judge it on the React side.

```tsx
const reduced = useReducedMotion(); // motion ライブラリのフック等
<motion.div animate={reduced ? {} : { y: [20, 0], opacity: [0, 1] }} />
```

---

## 8. New WCAG 2.2 criteria (the diff to grasp in 2026)

Of the 9 criteria added in WCAG 2.2, here's an implementation-viewpoint summary of the main ones affecting AA.

| Criterion | Level | What to do in implementation |
| ---- | ------ | -------------- |
| 2.4.11 Focus Not Obscured (Minimum) | AA | a fixed header or cookie banner doesn't completely hide the focused element |
| 2.5.7 Dragging Movements | AA | provide an alternative means such as a click for operations done by drag |
| 2.5.8 Target Size (Minimum) | AA | click targets are in principle 24×24px or more, or ensure sufficient spacing |
| 3.2.6 Consistent Help | A | place help paths like inquiries in the same position/order across pages |
| 3.3.7 Redundant Entry | A | don't make the user re-enter the same info in the same procedure (autocomplete, carry-over) |
| 3.3.8 Accessible Authentication (Minimum) | AA | don't require password memorization or puzzles. Allow password paste and passkeys |

In particular, **2.5.8 Target Size** and **3.3.8 Accessible Authentication** are often overlooked on existing sites. A cluster of small icon buttons, a paste-forbidden password field, and a CAPTCHA-required login all become problems in 2.2.

---

## 9. Testing strategy: a two-tier setup of automated + manual

Automated inspection alone can detect only 30–50% of a11y problems. **Protecting the foundation automatically and confirming the experience manually** is the right answer.

### Automated: build axe into CI

```ts
// tests/e2e/a11y.spec.ts （Playwright + @axe-core/playwright）
import { test, expect } from "@playwright/test";
import AxeBuilder from "@axe-core/playwright";

test("トップページに重大な a11y 違反がない", async ({ page }) => {
  await page.goto("/");
  const results = await new AxeBuilder({ page })
    .withTags(["wcag2a", "wcag2aa", "wcag22aa"])
    .analyze();
  expect(results.violations).toEqual([]);
});
```

During development, detect statically with `eslint-plugin-jsx-a11y`, and use `jest-axe` / `vitest-axe` per component, and you can crush problems early and cheaply (shift left).

### Manual: things only a human can tell

- **All operations by keyboard alone.** Pull out the mouse and go around with Tab, Enter, arrows, Escape.
- **Screen reader.** Listen to the main flows with macOS VoiceOver and Windows NVDA.
- **Magnified display.** Check that the layout doesn't break at 200% zoom and 400%-equivalent.

---

## 10. Anti-patterns (common failures)

- ❌ Removing the focus ring with `outline: none` (keyboard users get lost).
- ❌ Putting only `onClick` on `div`/`span` to make a button (focus, keyboard, role all missing).
- ❌ Using `placeholder` as a label substitute.
- ❌ Piling on meaningless `role` or `aria-*` ("ARIA for now" becomes harmful).
- ❌ Not distinguishing decorative images that should have an empty `alt` from images with content (decoration is `alt=""`, meaningful images get a description).
- ❌ Not doing a focus trap and return on modals.
- ❌ Ignoring `prefers-reduced-motion` in animations.

---

## 11. FAQ (frequently asked questions)

**Q. Which level (A/AA/AAA) should I aim for?**
A. The practical standard is AA. Many procurements and guidelines use AA as the baseline. AAA is realistically aimed at partially, for some criteria only.

**Q. Can I say I'm WCAG-compliant if I pass axe (automated inspection)?**
A. No. Automated inspection covers only the mechanically-judgeable range (about 30–50%). The experience with keyboard operation and screen readers needs manual confirmation.

**Q. Does accessibility support help SEO?**
A. It does. Heading structure, alt text, semantic HTML, and clear link text are information sources both search engines and assistive technology read.

**Q. Where do I start with a11y improvement of an existing site?**
A. The order of ① replacing with semantic HTML, ② keyboard operation and focus visualization, ③ form labels and errors, ④ contrast is cost-effective. Grasp the current state with automated inspection, then prioritize.

**Q. Is a11y perfect if I use a headless UI (Radix, etc.)?**
A. The foundation greatly improves but it's not perfect. Labeling, contrast, content structure, and path consistency remain the app side's responsibility.

---

## Conclusion: accessibility is the "total score of quality"

Accessibility is not a special specialty area but **an accumulation of good HTML, good state management, and good focus design.** That's exactly why a product with a11y guaranteed has high overall quality.

1. **Fully use semantic HTML** (the biggest lever).
2. **Guarantee keyboard operation and focus management** across all paths.
3. **Use ARIA as a supplement** correctly (misuse is worse than none).
4. **Leave no one behind with forms, contrast, and motion.**
5. **Reflect the new WCAG 2.2 criteria** (target size, accessible authentication, etc.).
6. **Continuously protect with automated + manual testing.**

These directly tie to expanding the reachable user count, meeting procurement requirements, improving SEO, and the trust of "carefully made."

**If you need accessible design of a new product, or WCAG 2.2 (AA) support / improvement of an existing site, feel free to reach out.** The case study below introduces the process of designing and implementing a business system used daily by diverse on-site users, balancing operability and quality.
