- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 1. なぜアクセシビリティが重要(じゅうよう)か
- 2. WCAG 2.2:4つの原則(げんそく)
- 3. セマンティックHTML:アクセシビリティの基礎(きそ)
- 4. ARIA:アクセシブルな意味(いみ)の追加(ついか)
- 5. キーボードアクセシビリティ
- 6. 色(いろ)とコントラスト
- 7. 画像(がぞう)とメディア
- 8. フォームのアクセシビリティ
- 9. React/Next.jsアクセシビリティパターン
- 10. テストと自動化(じどうか)
- 11. 法的要件(ほうてきようけん)と規制(きせい)
- 12. クイズ
- 参考資料(さんこうしりょう)
1. なぜアクセシビリティが重要(じゅうよう)か
10億人(おくにん)のユーザー
世界人口(せかいじんこう)の約(やく)15%、約(やく)10億人(おくにん)が何(なん)らかの障害(しょうがい)を持(も)っています。ウェブアクセシビリティは単(たん)に「良(よ)いこと」ではなく、ビジネス上(じょう)の必須事項(ひっすじこう)です。
| 障害(しょうがい)の種類(しゅるい) | 世界人口(せかいじんこう) | ウェブへの影響(えいきょう) |
|---|---|---|
| 視覚障害(しかくしょうがい) | 2.2億人(おくにん) | スクリーンリーダー、拡大鏡(かくだいきょう)使用(しよう) |
| 聴覚障害(ちょうかくしょうがい) | 4.66億人(おくにん) | 字幕(じまく)、手話(しゅわ)が必要(ひつよう) |
| 運動障害(うんどうしょうがい) | 数億人(すうおくにん) | キーボード、音声入力(おんせいにゅうりょく) |
| 認知障害(にんちしょうがい) | 様々(さまざま) | シンプルなUI、明確(めいかく)な言語(げんご) |
法的要件(ほうてきようけん)
- 米国(べいこく)ADA: ウェブサイトも「公共施設(こうきょうしせつ)」と見(み)なされる。訴訟(そしょう)が急増(きゅうぞう)
- EU EAA (European Accessibility Act): 2025年(ねん)6月(がつ)施行(しこう)。デジタルサービスに義務(ぎむ)
- 韓国障害者差別禁止法(かんこくしょうがいしゃさべつきんしほう): ウェブアクセシビリティ義務(ぎむ)。公共機関(こうきょうきかん)から民間(みんかん)へ拡大(かくだい)
- WCAG 2.2: 国際標準(こくさいひょうじゅん)。ほとんどの法律(ほうりつ)がAAレベルを要求(ようきゅう)
ビジネス価値(かち)
アクセシビリティはコストではなく投資(とうし)です。
- SEO改善(かいぜん): セマンティックHTMLとalt テキストは検索(けんさく)エンジンに好(この)まれる
- ユーザーベース拡大(かくだい): 全人口(ぜんじんこう)の15%にリーチ
- 法的(ほうてき)リスク軽減(けいげん): 事前投資(じぜんとうし)は訴訟費用(そしょうひよう)よりはるかに安価(あんか)
- 全(すべ)てのユーザーのUX向上(こうじょう): キーボードショートカット、明確(めいかく)なラベルは全員(ぜんいん)に有益(ゆうえき)
2. WCAG 2.2:4つの原則(げんそく)
POUR原則(げんそく)
WCAGは4つの核心原則(かくしんげんそく)POUR(Perceivable、Operable、Understandable、Robust)に基(もと)づいています。
| 原則(げんそく) | 意味(いみ) | 例(れい) |
|---|---|---|
| Perceivable(知覚可能(ちかくかのう)) | 情報(じょうほう)を知覚(ちかく)できること | altテキスト、字幕(じまく)、色(いろ)のコントラスト |
| Operable(操作可能(そうさかのう)) | UIを操作(そうさ)できること | キーボードアクセス、十分(じゅうぶん)な時間(じかん)、発作防止(ほっさぼうし) |
| Understandable(理解可能(りかいかのう)) | コンテンツとUIを理解(りかい)できること | 明確(めいかく)な言語(げんご)、一貫(いっかん)したナビゲーション |
| Robust(堅牢(けんろう)) | 多様(たよう)な技術(ぎじゅつ)で動作(どうさ)すること | 有効(ゆうこう)なHTML、ARIA互換性(ごかんせい) |
適合(てきごう)レベル
- レベルA: 最小要件(さいしょうようけん)(必須(ひっす))
- レベルAA: ほとんどの法律(ほうりつ)が要求(ようきゅう)する水準(すいじゅん)(推奨目標(すいしょうもくひょう))
- レベルAAA: 最高水準(さいこうすいじゅん)(サイト全体(ぜんたい)への適用(てきよう)は困難(こんなん))
WCAG 2.2の新(あたら)しい成功基準(せいこうきじゅん)
WCAG 2.2は2023年(ねん)10月(がつ)に公開(こうかい)され、以下(いか)の新(あたら)しい基準(きじゅん)が追加(ついか)されました。
| 成功基準(せいこうきじゅん) | レベル | 説明(せつめい) |
|---|---|---|
| 2.4.11 Focus Not Obscured (Minimum) | AA | フォーカスされた要素(ようそ)が他(た)のコンテンツに完全(かんぜん)に隠(かく)されてはならない |
| 2.4.12 Focus Not Obscured (Enhanced) | AAA | フォーカスされた要素(ようそ)が部分的(ぶぶんてき)にも隠(かく)されてはならない |
| 2.4.13 Focus Appearance | AAA | フォーカスインジケータのサイズとコントラスト要件(ようけん) |
| 2.5.7 Dragging Movements | AA | ドラッグが必要(ひつよう)な機能(きのう)に代替手段(だいたいしゅだん)を提供(ていきょう) |
| 2.5.8 Target Size (Minimum) | AA | タッチターゲットが最低(さいてい)24x24 CSSピクセル |
| 3.2.6 Consistent Help | A | ヘルプメカニズムが一貫(いっかん)した位置(いち)に |
| 3.3.7 Redundant Entry | A | 以前入力(いぜんにゅうりょく)した情報(じょうほう)を再要求(さいようきゅう)しない |
| 3.3.8 Accessible Authentication | AA | 認知機能(にんちきのう)テストなしの認証(にんしょう) |
| 3.3.9 Accessible Authentication (Enhanced) | AAA | より厳格(げんかく)な認証(にんしょう)アクセシビリティ |
3. セマンティックHTML:アクセシビリティの基礎(きそ)
正(ただ)しい要素(ようそ)の使用(しよう)
セマンティックHTMLはアクセシビリティの80%を解決(かいけつ)します。スクリーンリーダーはHTML要素(ようそ)の意味(いみ)を理解(りかい)します。
<!-- 悪い例:divで全てを作る -->
<div class="button" onclick="submit()">送信</div>
<div class="header">サイトタイトル</div>
<div class="nav">
<div class="link" onclick="goto('/')">ホーム</div>
</div>
<!-- 良い例:セマンティック要素を使用 -->
<button type="submit">送信</button>
<header><h1>サイトタイトル</h1></header>
<nav>
<a href="/">ホーム</a>
</nav>
<button>は自動的(じどうてき)にキーボードフォーカスを受(う)け取(と)り、Enter/Spaceで有効化(ゆうこうか)され、スクリーンリーダーが「ボタン」と認識(にんしき)します。<div>ではこれらすべてを手動(しゅどう)で実装(じっそう)する必要(ひつよう)があります。
ランドマーク
<body>
<header>
<nav aria-label="メインナビゲーション">...</nav>
</header>
<main>
<article>
<h1>記事タイトル</h1>
<section aria-labelledby="section-1">
<h2 id="section-1">セクション1</h2>
...
</section>
</article>
<aside aria-label="関連リンク">...</aside>
</main>
<footer>...</footer>
</body>
スクリーンリーダーのユーザーはランドマーク間(かん)を素早(すばや)く移動(いどう)できます。VoiceOverのRotorを使(つか)えば、header、nav、main、footerへ即座(そくざ)にジャンプできます。
見出(みだ)し階層構造(かいそうこうぞう)
<!-- 悪い例:見出しレベルのスキップ -->
<h1>ページタイトル</h1>
<h3>サブセクション</h3> <!-- h2をスキップ! -->
<h5>詳細項目</h5> <!-- h4をスキップ! -->
<!-- 良い例:順次的な見出し構造 -->
<h1>ページタイトル</h1>
<h2>セクションA</h2>
<h3>サブセクションA-1</h3>
<h3>サブセクションA-2</h3>
<h2>セクションB</h2>
<h3>サブセクションB-1</h3>
スクリーンリーダーユーザーの67%が見出(みだ)しでページを探索(たんさく)します。見出(みだ)しレベルをスキップすると文書構造(ぶんしょこうぞう)の把握(はあく)が困難(こんなん)になります。
4. ARIA:アクセシブルな意味(いみ)の追加(ついか)
ARIAの第一(だいいち)ルール
ARIAの第一(だいいち)ルール:ARIAを使(つか)わないこと。 ネイティブHTML要素(ようそ)で十分(じゅうぶん)な場合(ばあい)、ARIAは不要(ふよう)です。
<!-- ARIA不要:ネイティブ要素で十分 -->
<button>削除</button> <!-- role="button" 不要 -->
<input type="checkbox" /> <!-- role="checkbox" 不要 -->
<nav> <!-- role="navigation" 不要 -->
<!-- ARIA必要:ネイティブ要素がない場合 -->
<div role="tablist">
<button role="tab" aria-selected="true">タブ1</button>
<button role="tab" aria-selected="false">タブ2</button>
</div>
<div role="tabpanel">タブ1のコンテンツ</div>
主要(しゅよう)なARIA属性(ぞくせい)
<!-- aria-label: 視覚的テキストのない要素にラベルを提供 -->
<button aria-label="メニューを閉じる">
<svg><!-- Xアイコン --></svg>
</button>
<!-- aria-labelledby: 他の要素のテキストをラベルとして参照 -->
<h2 id="cart-heading">ショッピングカート</h2>
<ul aria-labelledby="cart-heading">
<li>商品1</li>
<li>商品2</li>
</ul>
<!-- aria-describedby: 追加説明の関連付け -->
<input
type="password"
aria-describedby="pw-hint"
/>
<p id="pw-hint">8文字以上、特殊文字を含む</p>
<!-- aria-live: 動的コンテンツの変更を通知 -->
<div aria-live="polite">
カートに3つの商品があります。
</div>
<!-- aria-expanded: 展開/折りたたみ状態 -->
<button aria-expanded="false" aria-controls="menu">
メニュー
</button>
<ul id="menu" hidden>...</ul>
<!-- aria-hidden: スクリーンリーダーから非表示 -->
<span aria-hidden="true">🔥</span>
<span class="sr-only">人気</span>
ライブリージョン
<!-- aria-live="polite": 現在の読み上げ完了後に通知 -->
<div aria-live="polite" aria-atomic="true">
検索結果:42件
</div>
<!-- aria-live="assertive": 即座に通知(エラーなど) -->
<div role="alert" aria-live="assertive">
セッションが期限切れです。再度ログインしてください。
</div>
<!-- role="status": ステータスメッセージ(politeと類似) -->
<div role="status">
ファイルアップロード完了
</div>
<!-- role="log": チャットメッセージなど -->
<div role="log" aria-live="polite">
<!-- 新しいメッセージが追加される -->
</div>
5. キーボードアクセシビリティ
フォーカス管理(かんり)の基本(きほん)
すべてのインタラクティブ要素(ようそ)はキーボードでアクセス可能(かのう)でなければなりません。
| キー | 動作(どうさ) |
|---|---|
| Tab | 次(つぎ)のフォーカス可能(かのう)な要素(ようそ)へ移動(いどう) |
| Shift + Tab | 前(まえ)のフォーカス可能(かのう)な要素(ようそ)へ移動(いどう) |
| Enter | リンクの有効化(ゆうこうか)、ボタンのクリック |
| Space | ボタンのクリック、チェックボックスのトグル |
| Escape | モーダル/ポップアップを閉(と)じる |
| 矢印(やじるし)キー | メニュー、タブ、ラジオグループ内(ない)の移動(いどう) |
スキップリンク
<!-- ページの最上部に配置 -->
<a href="#main-content" class="skip-link">
メインコンテンツへスキップ
</a>
<nav>
<!-- 長いナビゲーションメニュー -->
</nav>
<main id="main-content" tabindex="-1">
<!-- メインコンテンツ -->
</main>
.skip-link {
position: absolute;
left: -9999px;
top: auto;
width: 1px;
height: 1px;
overflow: hidden;
}
.skip-link:focus {
position: fixed;
top: 10px;
left: 10px;
width: auto;
height: auto;
padding: 12px 24px;
background: #000;
color: #fff;
z-index: 9999;
font-size: 1rem;
}
フォーカストラップ
モーダルが開(ひら)いているとき、フォーカスはモーダル内(ない)でのみ循環(じゅんかん)しなければなりません。
function useFocusTrap(containerRef: React.RefObject<HTMLElement>) {
useEffect(() => {
const container = containerRef.current
if (!container) return
const focusableSelector = [
'a[href]',
'button:not([disabled])',
'input:not([disabled])',
'select:not([disabled])',
'textarea:not([disabled])',
'[tabindex]:not([tabindex="-1"])',
].join(', ')
const focusableElements = container.querySelectorAll(focusableSelector)
const firstElement = focusableElements[0] as HTMLElement
const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement
function handleKeyDown(e: KeyboardEvent) {
if (e.key !== 'Tab') return
if (e.shiftKey) {
if (document.activeElement === firstElement) {
e.preventDefault()
lastElement.focus()
}
} else {
if (document.activeElement === lastElement) {
e.preventDefault()
firstElement.focus()
}
}
}
container.addEventListener('keydown', handleKeyDown)
firstElement?.focus()
return () => container.removeEventListener('keydown', handleKeyDown)
}, [containerRef])
}
タブ順序管理(じゅんじょかんり)
<!-- tabindexの値 -->
<!-- tabindex="0": 自然な順序に含める -->
<div role="button" tabindex="0">カスタムボタン</div>
<!-- tabindex="-1": プログラム的にのみフォーカス可能 -->
<div id="error-message" tabindex="-1">エラーが発生しました!</div>
<!-- 正の値のtabindexは使用禁止!順序が崩れます -->
<!-- 悪い例: tabindex="1", tabindex="2", tabindex="3" -->
6. 色(いろ)とコントラスト
コントラスト比(ひ)の要件(ようけん)
| テキストの種類(しゅるい) | AAレベル | AAAレベル |
|---|---|---|
| 通常(つうじょう)テキスト(14px未満(みまん)) | 4.5:1 | 7:1 |
| 大(おお)きいテキスト(18px以上(いじょう)または14pxボールド) | 3:1 | 4.5:1 |
| UIコンポーネント、グラフィック | 3:1 | - |
CSSでコントラストを確保(かくほ)
/* 良いコントラスト: #333 on #fff = 12.63:1 */
body {
color: #333333;
background-color: #ffffff;
}
/* リンク: 周囲テキストと3:1のコントラスト + 下線または他の視覚的手がかり */
a {
color: #0066cc;
text-decoration: underline;
}
/* フォーカスインジケータ: 3:1のコントラストが必須 */
:focus-visible {
outline: 3px solid #1a73e8;
outline-offset: 2px;
}
/* ダークモードでもコントラストを維持 */
@media (prefers-color-scheme: dark) {
body {
color: #e0e0e0;
background-color: #121212;
}
a {
color: #8ab4f8;
}
}
色覚異常(しきかくいじょう)への対応(たいおう)
/* 色だけで情報を伝えない */
/* 悪い例: 色だけでエラーを示す */
.error-field {
border-color: red;
}
/* 良い例: 色 + アイコン + テキスト */
.error-field {
border-color: #d32f2f;
border-width: 2px;
}
.error-field::before {
content: "⚠ ";
}
.error-message {
color: #d32f2f;
font-weight: bold;
}
コントラスト確認(かくにん)ツール
- Chrome DevTools: 要素(ようそ)の検査時(けんさじ)にコントラスト比(ひ)を表示(ひょうじ)
- axe DevTools: ページ全体(ぜんたい)のコントラスト監査(かんさ)
- Colour Contrast Analyser (CCA): スタンドアロンツール
- Stark: Figma/Sketchプラグイン
7. 画像(がぞう)とメディア
altテキストガイド
<!-- 情報性画像: 内容を説明 -->
<img src="chart.png" alt="2025年の売上推移:第1四半期100万、第2四半期150万、第3四半期200万" />
<!-- 装飾的画像: 空のalt -->
<img src="decorative-line.png" alt="" />
<!-- 機能性画像(リンク/ボタン): 動作を説明 -->
<a href="/home">
<img src="logo.png" alt="ホームページへ移動" />
</a>
<!-- 複雑な画像: 長い説明を提供 -->
<figure>
<img src="infographic.png" alt="アクセシビリティ統計インフォグラフィック" aria-describedby="info-desc" />
<figcaption id="info-desc">
世界で10億人が障害を持っており、
ウェブサイトの97%がアクセシビリティエラーを含んでいます。
最も一般的なエラーは低い色のコントラスト(83%)です。
</figcaption>
</figure>
<!-- SVGのアクセシビリティ -->
<svg role="img" aria-labelledby="svg-title">
<title id="svg-title">ダウンロードアイコン</title>
<path d="..." />
</svg>
動画(どうが)のアクセシビリティ
<video controls>
<source src="tutorial.mp4" type="video/mp4" />
<!-- 字幕(キャプション) -->
<track kind="captions" src="captions-ja.vtt" srclang="ja" label="日本語" default />
<track kind="captions" src="captions-en.vtt" srclang="en" label="English" />
<!-- 音声解説 -->
<track kind="descriptions" src="descriptions-ja.vtt" srclang="ja" label="音声解説" />
</video>
音声(おんせい)コンテンツ
すべての音声(おんせい)コンテンツにはテキスト代替(だいたい)(トランスクリプト)が必要(ひつよう)です。
8. フォームのアクセシビリティ
ラベルと入力(にゅうりょく)の関連付(かんれんづ)け
<!-- 方法1: for/id関連付け(推奨) -->
<label for="email">メールアドレス</label>
<input type="email" id="email" name="email" autocomplete="email" />
<!-- 方法2: labelで囲む -->
<label>
メールアドレス
<input type="email" name="email" autocomplete="email" />
</label>
<!-- 方法3: aria-labelledby -->
<span id="email-label">メールアドレス</span>
<input type="email" aria-labelledby="email-label" autocomplete="email" />
エラーメッセージとバリデーション
<div class="form-group">
<label for="password">パスワード</label>
<input
type="password"
id="password"
aria-describedby="pw-requirements pw-error"
aria-invalid="true"
autocomplete="new-password"
/>
<p id="pw-requirements" class="hint">
8文字以上、大文字・小文字・数字・特殊文字を含む
</p>
<p id="pw-error" class="error" role="alert">
パスワードが要件を満たしていません。
</p>
</div>
必須(ひっす)フィールド
<!-- aria-required + 視覚的表示 -->
<label for="name">
名前 <span aria-hidden="true" class="required">*</span>
</label>
<input
type="text"
id="name"
required
aria-required="true"
autocomplete="name"
/>
<p class="form-note">*は必須項目です</p>
オートコンプリート
<!-- WCAG 1.3.5: autocomplete属性の使用 -->
<input type="text" autocomplete="given-name" /> <!-- 名 -->
<input type="text" autocomplete="family-name" /> <!-- 姓 -->
<input type="email" autocomplete="email" /> <!-- メール -->
<input type="tel" autocomplete="tel" /> <!-- 電話番号 -->
<input type="text" autocomplete="street-address" /> <!-- 住所 -->
9. React/Next.jsアクセシビリティパターン
SPAでのフォーカス管理(かんり)
SPA(Single Page Application)では、ページ遷移時(せんいじ)にフォーカスが自動的(じどうてき)に移動(いどう)しません。
// ルート変更時にフォーカスを移動
function useRouteAnnounce() {
const pathname = usePathname()
useEffect(() => {
// メインコンテンツへフォーカスを移動
const main = document.querySelector('main')
if (main) {
main.setAttribute('tabindex', '-1')
main.focus()
}
}, [pathname])
return (
<div
role="status"
aria-live="polite"
className="sr-only"
>
ページが読み込まれました
</div>
)
}
ルート変更(へんこう)の通知(つうち)
// Next.js App Router: ルート変更の通知
'use client'
import { usePathname } from 'next/navigation'
import { useEffect, useState } from 'react'
function RouteAnnouncer() {
const pathname = usePathname()
const [announcement, setAnnouncement] = useState('')
useEffect(() => {
const pageTitle = document.title
setAnnouncement(`${pageTitle}ページに移動しました`)
}, [pathname])
return (
<div
role="status"
aria-live="assertive"
aria-atomic="true"
className="sr-only"
>
{announcement}
</div>
)
}
アクセシブルなモーダル(Dialog)
'use client'
import { useEffect, useRef } from 'react'
interface DialogProps {
isOpen: boolean
onClose: () => void
title: string
children: React.ReactNode
}
function AccessibleDialog({ isOpen, onClose, title, children }: DialogProps) {
const dialogRef = useRef<HTMLDialogElement>(null)
const previousFocusRef = useRef<HTMLElement | null>(null)
useEffect(() => {
const dialog = dialogRef.current
if (!dialog) return
if (isOpen) {
previousFocusRef.current = document.activeElement as HTMLElement
dialog.showModal()
} else {
dialog.close()
previousFocusRef.current?.focus()
}
}, [isOpen])
return (
<dialog
ref={dialogRef}
aria-labelledby="dialog-title"
onClose={onClose}
>
<h2 id="dialog-title">{title}</h2>
{children}
<button onClick={onClose}>閉じる</button>
</dialog>
)
}
Radix UI / Headless UIの活用(かつよう)
import * as Dialog from '@radix-ui/react-dialog'
// Radix UIはアクセシビリティを自動的に処理します
function MyDialog() {
return (
<Dialog.Root>
<Dialog.Trigger asChild>
<button>プロフィール編集</button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="overlay" />
<Dialog.Content className="content">
<Dialog.Title>プロフィール編集</Dialog.Title>
<Dialog.Description>
プロフィール情報を変更してください。
</Dialog.Description>
{/* フォームフィールド */}
<Dialog.Close asChild>
<button aria-label="閉じる">X</button>
</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
)
}
スクリーンリーダー専用(せんよう)テキスト
/* sr-onlyユーティリティクラス */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
// 使用例
<button>
<TrashIcon />
<span className="sr-only">アイテムを削除</span>
</button>
<a href="/cart">
<CartIcon />
<span className="sr-only">ショッピングカート(3つの商品)</span>
</a>
10. テストと自動化(じどうか)
axe-coreで自動(じどう)テスト
// jest + axe-core
import { render } from '@testing-library/react'
import { axe, toHaveNoViolations } from 'jest-axe'
expect.extend(toHaveNoViolations)
describe('Button', () => {
it('アクセシビリティ違反がないこと', async () => {
const { container } = render(<Button>クリック</Button>)
const results = await axe(container)
expect(results).toHaveNoViolations()
})
})
Playwright + axe統合(とうごう)テスト
import { test, expect } from '@playwright/test'
import AxeBuilder from '@axe-core/playwright'
test('ホームページのアクセシビリティ', async ({ page }) => {
await page.goto('/')
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag22aa'])
.analyze()
expect(accessibilityScanResults.violations).toEqual([])
})
// キーボードナビゲーションテスト
test('キーボードでメニューを操作', async ({ page }) => {
await page.goto('/')
// Tabでスキップリンクへ移動
await page.keyboard.press('Tab')
const skipLink = page.getByText('メインコンテンツへスキップ')
await expect(skipLink).toBeFocused()
// Enterでスキップリンクを有効化
await page.keyboard.press('Enter')
const main = page.locator('main')
await expect(main).toBeFocused()
})
Lighthouseアクセシビリティスコア
# CIでLighthouseを実行
npx lighthouse http://localhost:3000 \
--only-categories=accessibility \
--output=json \
--output-path=./lighthouse-report.json
// CIパイプラインでアクセシビリティスコアを確認
const report = JSON.parse(fs.readFileSync('./lighthouse-report.json', 'utf-8'))
const accessibilityScore = report.categories.accessibility.score * 100
if (accessibilityScore < 90) {
console.error(`アクセシビリティスコア ${accessibilityScore}点 - 90点以上が必要です`)
process.exit(1)
}
スクリーンリーダーの手動(しゅどう)テストチェックリスト
| タスク | VoiceOver (Mac) | NVDA (Windows) | TalkBack (Android) |
|---|---|---|---|
| ページタイトルの読(よ)み上(あ)げ | Cmd + F5 | Insert + T | 自動(じどう) |
| ランドマーク探索(たんさく) | Rotor (VO + U) | D/Shift+D | スワイプ |
| 見出(みだ)し探索(たんさく) | VO + Cmd + H | H/Shift+H | スワイプ |
| フォームフィールド | VO + Tab | Tab | タッチ探索(たんさく) |
| リンク一覧(いちらん) | Rotor | Insert + F7 | メニュー |
11. 法的要件(ほうてきようけん)と規制(きせい)
米国(べいこく):ADAとSection 508
- ADA Title III: ウェブサイトは「公共施設(こうきょうしせつ)」に該当(がいとう)。WCAG 2.1 AA要求(ようきゅう)
- Section 508: 連邦政府(れんぽうせいふ)ウェブサイトに義務(ぎむ)。WCAG 2.0 AA基準(きじゅん)
- 訴訟動向(そしょうどうこう): 2023年(ねん)にウェブアクセシビリティ訴訟(そしょう)4,600件以上(いじょう)
EU:European Accessibility Act (EAA)
- 2025年(ねん)6月(がつ)28日(にち) 施行(しこう)
- デジタルサービスを提供(ていきょう)する企業(きぎょう)が対象(たいしょう)
- WCAG 2.1 AA以上(いじょう)を要求(ようきゅう)
- 違反時(いはんじ)に罰金(ばっきん)
韓国(かんこく):障害者差別禁止法(しょうがいしゃさべつきんしほう)
- 障害者差別禁止(しょうがいしゃさべつきんし)および権利救済(けんりきゅうさい)に関(かん)する法律(ほうりつ)(2008年(ねん))
- ウェブアクセシビリティ認証(にんしょう)マーク
- 公共機関(こうきょうきかん)に義務(ぎむ)、民間(みんかん)セクターへ拡大(かくだい)
- KWCAG 2.2: WCAG 2.2に基(もと)づく
アクセシビリティステートメント
<!-- ウェブサイトにアクセシビリティステートメントを含めることを推奨 -->
<h1>アクセシビリティステートメント</h1>
<p>
私たちはWCAG 2.2 AA基準に準拠し、
すべてのユーザーにアクセシブルなウェブ体験を
提供することに取り組んでいます。
</p>
<p>
アクセシビリティに関する問題を発見された場合は、
<a href="mailto:a11y@example.com">a11y@example.com</a>まで
ご連絡ください。
</p>
12. クイズ
Q1. WCAGの4つの原則(げんそく)(POUR)を説明(せつめい)し、それぞれの例(れい)を挙(あ)げてください。
Perceivable(知覚可能(ちかくかのう)):すべての情報(じょうほう)をユーザーに提示(ていじ)できなければなりません。画像(がぞう)のaltテキストの提供(ていきょう)、動画(どうが)への字幕(じまく)追加(ついか)が例(れい)です。Operable(操作可能(そうさかのう)):すべての機能(きのう)が操作(そうさ)できなければなりません。キーボードのみでの全(すべ)ての機能(きのう)の使用(しよう)、十分(じゅうぶん)な時間(じかん)の提供(ていきょう)が例(れい)です。Understandable(理解可能(りかいかのう)):コンテンツとUIを理解(りかい)できなければなりません。明確(めいかく)なエラーメッセージ、一貫(いっかん)したナビゲーションが例(れい)です。Robust(堅牢(けんろう)):多様(たよう)な技術(ぎじゅつ)で動作(どうさ)しなければなりません。有効(ゆうこう)なHTML、支援技術(しえんぎじゅつ)との互換性(ごかんせい)が例(れい)です。
Q2. ARIAの第一(だいいち)ルールとは何(なん)ですか、なぜ重要(じゅうよう)ですか?
ARIAの第一(だいいち)ルールは「ネイティブHTML要素(ようそ)で十分(じゅうぶん)な場合(ばあい)はARIAを使用(しよう)しないこと」です。例(たと)えば、button要素(ようそ)にrole="button"を追加(ついか)することは不要(ふよう)です。ネイティブHTML要素(ようそ)にはアクセシブルなセマンティクス、キーボード動作(どうさ)、フォーカス管理(かんり)がすでに組(く)み込(こ)まれているためです。ARIAを誤(あやま)って使用(しよう)すると、アクセシビリティを改善(かいぜん)するどころか悪化(あっか)させる可能性(かのうせい)があります。
Q3. コントラスト比(ひ)4.5:1と3:1はそれぞれどのような状況(じょうきょう)に適用(てきよう)されますか?
4.5:1は通常(つうじょう)サイズのテキスト(18px未満(みまん))に対(たい)するAAレベルの要件(ようけん)です。3:1は大(おお)きなテキスト(18px以上(いじょう)または14pxボールド)およびUIコンポーネント(ボタンの境界線(きょうかいせん)、入力(にゅうりょく)フィールドなど)に対(たい)するAAレベルの要件(ようけん)です。AAAレベルは通常(つうじょう)テキストに7:1、大(おお)きなテキストに4.5:1を要求(ようきゅう)します。
Q4. SPAでルート変更時(へんこうじ)のアクセシビリティをどのように保証(ほしょう)しますか?
SPAのクライアントサイドルーティングは、従来(じゅうらい)のページロードとは異(こと)なり、スクリーンリーダーに自動通知(じどうつうち)を提供(ていきょう)しません。解決策(かいけつさく)として、ルート変更時(へんこうじ)にメインコンテンツへのフォーカス移動(いどう)、aria-liveリージョンでの新(あたら)しいページタイトルの通知(つうち)、documentのtitle更新(こうしん)、スキップリンクの提供(ていきょう)があります。Next.jsのApp Routerは組(く)み込(こ)みのルート通知機能(つうちきのう)を提供(ていきょう)しています。
Q5. axe-coreをCI/CDパイプラインに統合(とうごう)する方法(ほうほう)とその限界(げんかい)は?
axe-coreはjest-axe(ユニットテスト)や@axe-core/playwright(E2Eテスト)でCIに統合(とうごう)できます。WCAGタグでフィルタリングして特定(とくてい)の基準(きじゅん)のみをテストし、違反(いはん)があればビルドを失敗(しっぱい)させます。限界(げんかい)として、自動(じどう)ツールはアクセシビリティの問題(もんだい)の約(やく)30-40%しか検出(けんしゅつ)できないという点(てん)があります。キーボードの使(つか)いやすさ、スクリーンリーダーとの互換性(ごかんせい)、認知的(にんちてき)アクセシビリティなどは手動(しゅどう)テストが必須(ひっす)です。
参考資料(さんこうしりょう)
- WCAG 2.2 - W3C Recommendation
- WAI-ARIA 1.2 Specification
- MDN Web Accessibility Guide
- A11y Project Checklist
- Deque axe-core
- WebAIM Million Report
- Radix UI Accessibility
- React Accessibility Docs
- Next.js Accessibility
- Inclusive Components by Heydon Pickering
- EU European Accessibility Act
- 韓国(かんこく)ウェブアクセシビリティ認証評価院(にんしょうひょうかいん)
- Chrome DevTools Accessibility
- Stark Accessibility Tools