アクセシビリティは「品質の総合点」が現れる場所です。雑なコンポーネント設計、状態管理の漏れ、フォーカスの放置——すべてが a11y の欠陥として表面化します。逆に言えば、a11y を担保できるチームは、コード品質そのものが高い。
1. なぜ今アクセシビリティなのか(ビジネスの観点)
技術論の前に、発注者にとっての価値を整理します。
- 対象ユーザーの拡大。 何らかの支援を必要とする人は人口の相当割合に上ります。a11y はそのまま到達可能なユーザー数の拡大です。
- 法令・調達要件。 公共・大企業の調達では WCAG 準拠が要件化されることが増えています。準拠していないと「そもそも候補に入れない」ことがあります。
- SEO との相乗効果。 適切な見出し構造、代替テキスト、セマンティックなマークアップは、検索エンジンとスクリーンリーダーの双方に効きます。a11y 改善は集客改善でもあります。
- 品質の証明。 アクセシブルなプロダクトは、設計が丁寧であることの証拠になります。発注判断の信頼材料になります。
つまり a11y は「コスト」ではなく「到達範囲・コンプライアンス・SEO・信頼」への投資です。
2. 土台:WCAG 2.2 の 4 原則(POUR)
WCAG はすべて 4 つの原則に集約されます。これを頭に入れておくと、個別テクニックが「なぜ必要か」で理解できます。
| 原則 | 意味 | 代表例 |
|---|---|---|
| Perceivable(知覚可能) | 情報が知覚できる | 画像の代替テキスト、十分なコントラスト |
| Operable(操作可能) | UI を操作できる | キーボード操作、十分なターゲットサイズ |
| Understandable(理解可能) | 内容と操作が理解できる | 一貫したナビ、明確なエラー |
| Robust(堅牢) | 支援技術が解釈できる | 妥当な HTML、適切な name/role/value |
レベルは A(最低限)< AA(標準)< AAA(最高)。実務の目標は AA です。
3. 最大のレバー:セマンティック HTML を使い切る
a11y の8割は、正しい要素を使うだけで達成できます。div と span でボタンやリンクを「作る」のをやめることが、最も費用対効果の高い改善です。
// ❌ アンチパターン:div をボタンに見せかける
<div className="btn" onClick={handleClick}>送信</div>
// ✅ button は「フォーカス可能・Enter/Space で発火・支援技術がボタンと認識」を無料で得る
<button type="button" onClick={handleClick}>送信</button>
押さえどころ:
- ページ遷移は
a(Next.js はLink)、アクションはbutton。 見た目で選ばず、意味で選ぶ。 - 見出しは階層を守る。
h1は1ページ1つ、h2 → h3を飛ばさない。スクリーンリーダー利用者は見出しでページを「拾い読み」します。 - ランドマークで領域を示す。
header/nav/main/footerを使うと、支援技術がページ構造を提示できます。mainは1つ。 - リストは
ul/ol、表はtable+th(scope付き)。 見た目だけ似せたdivは構造情報を失います。
セマンティック HTML は「後から ARIA で補う」より圧倒的に堅牢で、保守コストも低い。これは KISS そのものです。
4. キーボード操作とフォーカス管理
マウスを使えないユーザー(運動障害・スクリーンリーダー・パワーユーザー)にとって、キーボードだけで全機能に到達できることは生命線です。
4-1. すべての操作にキーボードで到達できるか
- Tab で論理的な順序に移動でき、Enter/Space/矢印で操作できること。
tabindexは0(フォーカス順に含める)か-1(プログラムでフォーカス可能)だけ。 正のtabindexはフォーカス順を壊すので使わない。- カスタムウィジェット(タブ、メニュー、コンボボックス)は WAI-ARIA Authoring Practices のキーボードパターンに従う。自作せず、Radix UI など検証済みのヘッドレス UI を使うのが安全で速い。
4-2. フォーカスは「見える」こと
/* ❌ 絶対にやってはいけない:フォーカスリングを消すだけ */
:focus { outline: none; }
/* ✅ マウス利用時は控えめ、キーボード利用時ははっきり見せる */
:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}
4-3. ルート遷移・モーダルでのフォーカス移動
SPA/App Router ではページ遷移してもフォーカスが置き去りになりがちです。モーダルでは「開いたら中へフォーカス、閉じたら開いた要素へ戻す、背後を Tab で触れない(フォーカストラップ)」が必須です。
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;
}
実務では、こうした挙動を完備した Radix UI の Dialog を使うのが最短かつ安全です(このサイトの UI も Radix ベースです)。車輪の再発明は YAGNI 違反になりがちです。
5. ARIA は「最後の手段」— 正しい使い方
ARIA は HTML だけでは表現できない動的な情報(展開状態、ライブ更新など)を補う道具です。乱用は逆効果になります。
ARIA の5つの鉄則(公式の "Rules of ARIA" の要約):
- ネイティブ HTML 要素があるなら、そちらを使う。
role="button"よりbutton。 - ネイティブの意味を ARIA で上書きしない。
<h2 role="tab">のような破壊をしない。 - すべてのインタラクティブな ARIA はキーボードで操作可能に。
- フォーカスできる要素を
role="presentation"/aria-hidden="true"で隠さない。(フォーカスできるのに支援技術から見えない、という矛盾を作らない) - すべての操作要素にアクセシブルな名前を付ける。
よく使う正しい例:
// アイコンのみボタンには必ずラベルを
<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. フォームのアクセシビリティ
フォームは a11y の失敗が最も多い場所です(詳細な実装はReact Hook Form × Zod 実践ガイドを参照)。最低限これだけは守ります。
- すべての入力に
label(htmlForで関連付け)。placeholderはラベルの代わりにならない(入力すると消え、コントラストも低い)。 - エラーは
aria-invalidとaria-describedbyで入力欄に紐づけ、role="alert"で通知。 - 必須は色だけに頼らない。 テキストや
*+凡例で示し、aria-requiredを付ける。 - 送信失敗時は最初のエラー項目へフォーカス。
<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. 色・コントラスト・モーション
色とコントラスト
- 本文テキストは背景とのコントラスト比 4.5:1 以上(大きい文字は 3:1)。AA の必須基準です。
- 色だけで情報を伝えない。 「赤=エラー」はアイコンやテキストと併用する(色覚特性への配慮)。
モーション(prefers-reduced-motion)
前庭障害のある人には、大きなアニメーションが体調不良を引き起こします。OS の「視差効果を減らす」設定を尊重します。このサイトのアニメーション層も prefers-reduced-motion を尊重する設計です。
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
React 側でも判定できます。
const reduced = useReducedMotion(); // motion ライブラリのフック等
<motion.div animate={reduced ? {} : { y: [20, 0], opacity: [0, 1] }} />
8. WCAG 2.2 の新基準(2026年に押さえるべき差分)
WCAG 2.2 で追加された 9 基準のうち、AA に影響する主要なものを実装観点で要約します。
| 基準 | レベル | 実装でやること |
|---|---|---|
| 2.4.11 フォーカスの非遮蔽(最低限) | AA | 固定ヘッダーや Cookie バナーがフォーカス中の要素を完全に隠さない |
| 2.5.7 ドラッグ動作 | AA | ドラッグで行う操作に、クリック等の代替手段を用意する |
| 2.5.8 ターゲットサイズ(最低限) | AA | クリック対象は原則 24×24px 以上、または十分な間隔を確保 |
| 3.2.6 一貫したヘルプ | A | 問い合わせ等のヘルプ導線をページ間で同じ位置・順序に |
| 3.3.7 冗長な入力 | A | 同一手続き中で同じ情報を再入力させない(自動補完・引き継ぎ) |
| 3.3.8 アクセシブルな認証(最低限) | AA | パスワード記憶やパズルを必須にしない。パスワード貼り付け・パスキーを許可 |
特に 2.5.8 ターゲットサイズ と 3.3.8 アクセシブルな認証 は、既存サイトが見落としがちです。小さなアイコンボタンの密集、貼り付け禁止のパスワード欄、CAPTCHA 必須のログインは、いずれも 2.2 で問題になります。
9. テスト戦略:自動 + 手動の二段構え
自動検査だけでは a11y 問題の3〜5割しか検出できません。自動で土台を守り、手動で体験を確認するのが正解です。
自動:axe を CI に組み込む
// 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([]);
});
開発中は eslint-plugin-jsx-a11y で静的に検出し、コンポーネント単位では jest-axe / vitest-axe を使うと、問題を早期に・安く潰せます(シフトレフト)。
手動:人にしか分からないこと
- キーボードだけで全操作。 マウスを抜いて Tab・Enter・矢印・Escape で一周できるか。
- スクリーンリーダー。 macOS の VoiceOver、Windows の NVDA で主要動線を聞く。
- 拡大表示。 200% ズーム、400% 相当でレイアウトが破綻しないか。
10. アンチパターン(やりがちな失敗)
- ❌
outline: noneでフォーカスリングを消す(キーボード利用者が迷子になる)。 - ❌
div/spanにonClickだけ付けてボタン化(フォーカス・キーボード・役割がすべて欠落)。 - ❌
placeholderをラベル代わりに使う。 - ❌ 意味のない
roleやaria-*を盛る(「とりあえず ARIA」は害になる)。 - ❌ 画像
altを空にすべき装飾画像と、内容のある画像を区別しない(装飾はalt=""、意味のある画像は説明を)。 - ❌ モーダルでフォーカストラップと復帰をしない。
- ❌ アニメーションで
prefers-reduced-motionを無視する。
11. FAQ(よくある質問)
Q. どのレベル(A/AA/AAA)を目標にすべき? A. 実務標準は AA です。多くの調達・ガイドラインが AA を基準にしています。AAA は一部基準のみ部分的に狙うのが現実的です。
Q. axe(自動検査)に通れば WCAG 準拠と言える? A. 言えません。自動検査は機械的に判定できる範囲(3〜5割程度)のみです。キーボード操作やスクリーンリーダーでの体験は手動確認が必要です。
Q. アクセシビリティ対応は SEO に効く? A. 効きます。見出し構造・代替テキスト・セマンティック HTML・明確なリンクテキストは、検索エンジンと支援技術の双方が読む情報源です。
Q. 既存サイトの a11y 改善はどこから手を付ける? A. ①セマンティック HTML への置換、②キーボード操作とフォーカス可視化、③フォームのラベル・エラー、④コントラスト、の順が費用対効果が高いです。自動検査で現状把握してから優先度を付けます。
Q. ヘッドレス UI(Radix 等)を使えば a11y は万全? A. 土台は大きく改善しますが万全ではありません。ラベル付与、コントラスト、コンテンツ構造、動線の一貫性はアプリ側の責任として残ります。
まとめ:アクセシビリティは「品質の総合点」
アクセシビリティは特別な専門領域ではなく、良い HTML・良い状態管理・良いフォーカス設計の積み重ねです。だからこそ、a11y が担保されたプロダクトは総合的な品質が高い。
- セマンティック HTML を使い切る(最大のレバー)。
- キーボード操作とフォーカス管理を全動線で保証する。
- ARIA は補助として正しく使う(誤用は無いほうがマシ)。
- フォーム・コントラスト・モーションで誰も取りこぼさない。
- WCAG 2.2 の新基準(ターゲットサイズ・アクセシブル認証など)を反映する。
- 自動 + 手動テストで継続的に守る。
これらは到達ユーザー数の拡大、調達要件の充足、SEO 改善、そして「丁寧に作られている」という信頼に直結します。
新規プロダクトのアクセシブルな設計、あるいは既存サイトの WCAG 2.2(AA)対応・改善が必要な場合は、お気軽にご相談ください。 下記の事例では、現場の多様なユーザーが日々使う業務システムを、操作性と品質を両立して設計・実装した過程を紹介しています。