スクリーンリーダ
画面の内容を音声で読み上げる支援技術。a11y を「実際の体験」として理解するには、 1度は自分で触ってみるのが一番早い。10分でメンタルモデルが変わる。
主要なスクリーンリーダ
| 名前 | OS | 備考 |
|---|---|---|
| NVDA | Windows | 無料・OSS。普及シェア No.1。 |
| JAWS | Windows | 有料。企業利用が多い。 |
| Narrator | Windows | OS 標準。 |
| VoiceOver | macOS / iOS | OS 標準。Cmd+F5 で起動。 |
| TalkBack | Android | OS 標準。 |
| Orca | Linux | OS 標準。 |
テストのためなら、VoiceOver(Mac)と NVDA(Windows)のどちらかが触れれば十分。
仕組み: アクセシビリティツリー
ブラウザは DOM をもとにアクセシビリティツリーを構築する。スクリーンリーダは DOM ではなくこのツリーを介してページを「読む」。
ツリーの各ノードは少なくとも以下を持つ:
- Name("メールアドレス")
- Role("テキスト入力")
- State("必須" "押下中" "展開済み")
- Value("foo@example.com")
Chrome DevTools の Elements パネル → Accessibility タブで実際のツリーが見える。 「自分の HTML が支援技術にどう見えているか」を確認できる。
SR ユーザーの読み方
視覚ユーザーがページを上から下に読むのと違い、SR 利用者はジャンプ機能を多用する。
- 見出しジャンプ — h1〜h6 をたどる(ページ構造の把握)
- ランドマークジャンプ — main / nav / footer 間で移動
- リンク一覧 — ページ内の全リンクを一覧
- フォーム要素一覧 — フォームを探す時
- 見出し → 段落 → 段落…と上から
だから「見出しがないページ」「意味のない見出し階層」「ランドマークがない」は 極端に使いにくくなる。
Mac VoiceOver 入門
とりあえず触ってみるならこれ。Cmd+F5 で ON/OFF。 Caps Lock はVoiceOver キー(VO)として使う(システム環境設定で設定)。
| キー | 動作 |
|---|---|
| VO+→ / ← | 次/前の要素へ移動 |
| VO+Space | クリック |
| VO+U | ローター(リンク・見出し一覧)を開く |
| VO+Cmd+H | 次の見出し |
| VO+Cmd+L | 次のリンク |
| Ctrl | 読み上げを止める |
| VO+A | ページ全体を読み上げ |
Safari + VoiceOver の組み合わせが一番安定。Chrome でもだいたい動く。
NVDA 入門(Windows)
無料で 公式サイトから DL。Insert キーを NVDA キーとして使う("NVDA")。
| キー | 動作 |
|---|---|
| ↓ / ↑ | 次/前の行(ブラウズモード) |
| H | 次の見出し |
| K | 次のリンク |
| F | 次のフォームコントロール |
| D | 次のランドマーク |
| NVDA+F7 | 要素一覧(リンク・見出し・ランドマーク) |
| NVDA+Down | そこから読み上げ |
| Ctrl | 停止 |
「見えない」と「読まれない」の違い
| 方法 | 視覚的に見える | SR が読み上げ | フォーカス可能 |
|---|---|---|---|
display: none | × | × | × |
visibility: hidden | × | × | × |
opacity: 0 | × | ○ | ○ |
hidden 属性 | × | × | × |
aria-hidden="true" | ○ | × | ○(要注意) |
inert 属性 | ○ | × | × |
.sr-only CSS | × | ○ | ○ |
モーダル背景は inert、装飾アイコンは aria-hidden="true"、
SR専用テキストは .sr-only ─ と用途で使い分ける。
ライブリージョン
ページの一部が動的に変わった時、SR にそれを通知する仕組み。 例: 「保存しました」のトースト、検索結果の件数表示、エラー通知。
aria-live の3段階
aria-live="off"(既定)— 通知しないaria-live="polite"— 今読み上げている内容が終わったら通知(控えめ)aria-live="assertive"— 即座に割り込んで通知(緊急)
専用 role を使う方が楽
| role | 同等の aria-live | 用途 |
|---|---|---|
role="status" | polite | 状態通知("保存しました") |
role="alert" | assertive | 緊急("エラー") |
role="log" | polite | 追加されていくログ(チャット) |
role="timer" | off | タイマー |
role="marquee" | off | スクロールテキスト |
<!-- 保存通知 -->
<div role="status">変更を保存しました</div>
<!-- エラー -->
<div role="alert">接続に失敗しました</div>
動的に追加すること
あらかじめ live region のコンテナを置いておき、中身を後から書き換えるのが安定パターン。 コンテナを後から DOM に挿入すると、SR が見落とすことがある。
<!-- 最初から空で置いておく -->
<div id="status" role="status"></div>
<script>
document.getElementById("status").textContent = "保存しました"
</script>
連続して同じ文言を出すと無視されることがある
SR は「同じ文字列の更新」を変化なしとみなして読み上げないことがある。 テキストを一度クリアしてから入れ直すと確実。
SPA でのページ遷移通知
SPA はクライアントサイドルーティング。ブラウザの「ページ読み込み完了」イベントが発火しないので、 SR は「ページが変わった」ことに気付けない。
対策:
- 遷移後に
<main>またはh1にプログラム的に focus を移す - またはライブリージョンに「○○ページに移動しました」と書き込む
// React Router 等を使うフレームワークではビルトインの focus 管理が望ましい
// 自前で書く時の最小例
function onRouteChange(title) {
document.title = title
const main = document.querySelector("main")
main.setAttribute("tabindex", "-1")
main.focus()
}
テストの仕方
最低限のチェック
- SR を ON にしてページを開く。タイトルが読まれるか。
- Tab で全要素に到達できるか。読み上げ内容に違和感がないか。
- 見出しジャンプ(H キー / VO+Cmd+H)でページ構造が伝わるか。
- ランドマークジャンプでヘッダ・main・フッタが認識できるか。
- フォームをSR で実際に入力。ラベル・必須・エラーが伝わるか。
- モーダルや menu を開いた時、フォーカスが正しく移っているか。
- 動的更新(保存通知等)が読み上げられるか。
Chrome DevTools の Accessibility パネル
要素を選んだ状態でElements → Accessibility。Name / Role / State が 意図通りになっているか確認できる。SR を起動しなくても素早くチェックできる。
初めて SR を ON にすると、画面が読み上げられて「どこに何があるかわからない」状態になる。
これが多くの SR 利用者の毎日の体験。
まずは自分のサイトで何回か触って慣れる。それだけで a11y への理解が劇的に深まる。