# 観点表: 機械可読性

支援技術（スクリーンリーダーなど）から、コンテンツの内容・構造・状態が正しく読み取れるかを
確認する観点。実ページでは Accessibility Visualizer 拡張機能やアクセシビリティツリーで、
ソースコードではマークアップで確認する。

**このスキルによる確認は、実機のスクリーンリーダーによる確認の代替にはならない。**
アクセシビリティツリーやマークアップが妥当に見えても、実際の読み上げでは問題が生じることが
ある。レポートには「スクリーンリーダー実機による確認は行っていない」ことを明記する。

---

### AXE-01: axe-core による自動チェック

- 確認手段: `page`
- 判定: axe-core を実行し、`violations` を確認する。対象とするタグは
  `wcag2a` `wcag2aa` `wcag21a` `wcag21aa` `wcag22aa` `best-practice`。
  - ベストプラクティスは、ただちに WCAG の達成基準に違反することを意味しないが、結果的に
    違反する状態である可能性や、利用者に何らかの不便をもたらしている可能性を示すものとして
    扱う
  - 状態によって画面が変化する場合（メニューの開閉、モーダルダイアログの表示など）には、
    **その都度実行する**
  - `incomplete` は「自動では判定できなかった」項目であり、手動確認の対象として
    後続の手順に引き継ぐ。特にコントラスト比の `incomplete` は VIS-09 で必ず確認する
- 注意: axe-core のドキュメントや axe DevTools の説明には、**筋の悪い解決方法も紹介されて
  いる**。修正方法の提案としてそのまま転記せず、対象の目的に沿った修正方法を自分で組み立てる。
- CI/CD で axe-core を実行している場合でも、設定漏れや表示状態の網羅漏れが起こりやすいため、
  チェック担当者はあらためて axe-core を実行する。

---

### SEM-01: 画像の代替テキスト

- WCAG: SC 1.1.1 (A)
- 確認手段: `both`
- 判定: 画像には、簡潔で必要充分な代替テキストが付けられていること。
  - 純粋な装飾画像など、その画像が知覚できなくなっても利用者への影響が一切ないと判断できる
    画像には、代替テキストは必要ない
  - アイコン画像と、そのアイコンが表す事柄を示すテキストが連続して配置されており、アイコンに
    代替テキストを付けると同じテキストが連続してしまう場合、代替テキストは必要ない
  - 代替テキストが不要な画像は、支援技術から隠されるようにする（`alt=""`、
    `aria-hidden="true"` など）。**属性そのものを書かないのは誤り**
  - 代替テキストの長さは、日本語の場合80文字程度までを目安とする。厳密に守る必要はないが、
    あまりに長い場合はテキストとしての配置を検討する
  - 判断には [Alt デシジョンツリー](https://www.w3.org/WAI/tutorials/images/decision-tree/ja)
    が参考になる
- `code` からの判定: `<img>` の `alt` 欠落、`alt="画像"` `alt="image"` のような無意味な値、
  ファイル名がそのまま入っているもの、意味を持つ `<svg>` に `role="img"` と
  アクセシブルネームがないもの、CSS の `background-image` で表現された意味のある画像を探す。
  代替テキストの**内容**が適切かは、周囲の文脈から判断する。判断できない場合は「判定不能」と
  して、製作者への確認事項に挙げる。
- 重篤度の目安: 画像が情報を伝えている、または操作要素であるなら Major。装飾画像に
  不要な代替テキストが付いている程度なら Minor。

### SEM-02: 情報および関係性

- WCAG: SC 1.3.1 (A)
- 確認手段: `both`
- 判定: 視覚的に表現されている構造や関係性が、支援技術でも読み取れるようにマークアップで
  表現されていること。
  - 見出しに見えるものは見出し要素（`<h1>`〜`<h6>`）である。見出しレベルが飛んでいない
  - 箇条書きに見えるものはリスト要素（`<ul>` `<ol>` `<dl>`）である
  - 表に見えるものは `<table>` であり、見出しセルは `<th>` で、必要に応じて `scope` が
    指定されている。レイアウト目的で `<table>` を使っていない
  - グループ化された入力欄（ラジオボタン群など）は `<fieldset>` と `<legend>`、または
    同等の `role="group"` と アクセシブルネームでまとめられている
  - 強調が `<strong>` `<em>` で表現されている（見た目だけの太字・斜体ではない）
- `code` からの判定: `<div class="heading">` のような見た目だけの見出し、`<br>` を並べた
  疑似的なリスト、`<table>` によるレイアウト、`font-weight: bold` だけの強調を探す。
- 重篤度の目安: 構造が読み取れずコンテンツを理解できないなら Major。それ以外は Normal。

### SEM-03: 入力欄とラベルの関連付け

- WCAG: SC 1.3.1 (A)
- 確認手段: `both`
- 判定: `<input>` `<textarea>` `<select>` などの入力欄には、その目的を示すテキストが表示されて
  いるべきであり、その場合 `<label>` などを使って紐付ける形でアクセシブルネームが
  付けられていること。
  - `<label for="...">` と `id` の対応、または `<label>` による囲み込みを使う
  - `aria-label` で表示ラベルと異なる文言を与えると SEM-08 の問題になる
  - `placeholder` はラベルの代わりにならない（入力すると消える、コントラストが低いことが多い）
- `code` からの判定: `id` / `for` の対応が取れていない `<label>`、ラベルのない入力欄、
  `placeholder` のみの入力欄を探す。コンポーネント化されている場合は、`id` が
  自動生成されて正しく紐付いているかを実装まで辿って確認する。
- 重篤度の目安: 入力欄の目的がわからずフォームを完了できないため、原則 Major。

### SEM-04: 意味のある順序

- WCAG: SC 1.3.2 (A)
- 確認手段: `both`
- 判定: コンテンツの順序に意味がある場合、その順序に従って記述される（スクリーンリーダーが
  読み上げる）こと。
- `code` からの判定: CSS の `order`、`flex-direction: *-reverse`、`grid-row` /
  `grid-column` による並べ替え、`position: absolute` による配置で、DOM 順序と視覚的な順序が
  食い違っている箇所を探す。KBD-07（フォーカス順序）と同じ原因であることが多い。
- よくある問題: たとえば、縦2列で1列目に項目名、2列目に項目名に紐付く内容が並べられるときには、
  項目名の羅列の後に内容の連続があると、項目名と内容の紐付きを認識することができない。
  この場合、項目名-内容の順序で並んでいるべきである（別途、適切な `role` やWAI-ARIA属性を設定するべきである）。
- 重篤度の目安: 読み上げ順で内容を理解できないなら Major。

### SEM-05: 入力の目的（autocomplete）

- WCAG: SC 1.3.5 (AA)
- 確認手段: `code`
- 判定: 利用者自身に関する情報を収集する `<input>` `<textarea>` `<select>` には、適切な
  `autocomplete` 属性が指定されていること。不必要に `autocomplete="off"` にしてはならない。
  - 氏名、メールアドレス、電話番号、住所、生年月日、クレジットカード情報、
    ユーザー名、パスワードなどが対象
  - 値は [HTML 仕様の autofill field name](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill)
    から選ぶ（`name` `email` `tel` `postal-code` `street-address` `current-password`
    `new-password` など）
- `code` からの判定: 対象となる入力欄への `autocomplete` の欠落、`autocomplete="off"` の
  指定、無効な値の指定を探す。
- よくある問題: セキュリティのつもりでパスワード欄に `autocomplete="off"` を指定し、
  パスワードマネージャーの利用を妨げている（SPEC-03 の問題にもなる）。
- 重篤度の目安: 自動入力に頼る利用者の入力負担が増えるため Normal。パスワード欄なら Major。

### SEM-06: 文字画像

- WCAG: SC 1.4.5 (AA)
- 確認手段: `both`
- 判定: 必要不可欠なもの（ロゴタイプ、紙の書類を見せるもの、図表内の文字など）を除いて、
  テキストを画像化して表示していないこと。
- `code` からの判定: 見出しやボタンのラベルが画像になっている箇所を探す。
  画像化されたテキストがある場合、そのコントラスト比は axe-core では検出できないため
  VIS-09 の手動確認対象にもなる。
- 重篤度の目安: 拡大時に文字がぼやけて読めない、文字色を変更できないため Normal。

### SEM-07: ランドマークと見出し

- WCAG: SC 2.4.1 (A) / SC 1.3.1 (A)
- 確認手段: `both`
- 判定: ページに適切にランドマークと見出しが設定されていること。特に、複数のページで共通する
  部分（ナビゲーションなど）ではない、そのページに固有の部分が `main` ランドマークになって
  いて、その冒頭に見出しが配置されていること。
  - `<main>` はページに1つ。`<header>` `<nav>` `<aside>` `<footer>` `<search>` を
    適切に使う
  - 同じ種類のランドマークが複数ある場合は、`aria-label` などで区別できるようにする
  - 見出しはページの構造を表す。`<h1>` から始まり、レベルを飛ばさない
  - スキップリンク（「本文へ」）があると、繰り返されるブロックを回避しやすい
- `code` からの判定: `<main>` の有無と数、`<div>` で組まれたヘッダー/ナビゲーション、
  見出しレベルの飛び、`<h1>` の欠落・重複を確認する。
- 重篤度の目安: 主要なコンテンツに到達しづらくなるため Normal。ランドマークも見出しも
  まったくない場合は Major。

### SEM-08: ラベル イン ネーム

- WCAG: SC 2.5.3 (A)
- 確認手段: `both`
- 判定: 入力欄やボタンなどのコンポーネントが、視覚的に表示されているテキストや文字画像の
  ラベルを持つ場合、そのコンポーネントのアクセシブルネームは表示されているラベルを
  **含む**こと。
  - ベストプラクティスは、表示ラベルをそのままアクセシブルネームとすることである。
    そのために `<input>` `<textarea>` `<select>` では `<label>` を使い、それ以外の要素で
    実装されたものでは `aria-labelledby` を使う
- `code` からの判定: 表示テキストと異なる `aria-label` が指定されている要素を探す。
  たとえば `<button aria-label="送信">申し込む</button>` は、音声入力の利用者が
  「申し込む」と発話しても操作できない。
- 重篤度の目安: 音声入力の利用者が操作できないため Major。

### SEM-09: ページの言語

- WCAG: SC 3.1.1 (A)
- 確認手段: `code`
- 判定: `<html>` 要素の `lang` 属性により、ページのデフォルトの言語が機械可読な形で
  宣言されていること。
  - `lang` 属性によって言語エンジンが切り替わるスクリーンリーダーを使うことで、この指定が
    成功していることを確認できる。指定できていない場合、不自然な読み上げになったり、
    まったく読み上げなくなったりする。ただし macOS や iOS の VoiceOver ではこの現象が
    起きないため、この方法では確認できない
  - Accessibility Visualizer では「ページの言語」として設定値を表示できる
- `code` からの判定: `<html lang="...">` の有無と値。**HTML テンプレートのデフォルトが
  `en` になっていることが多く、日本語のページ（`ja` を指定するべき）で `en` のままに
  なっていることが多い**。SPA やフレームワークでは、レイアウトファイルや設定
  （Next.js の `app/layout.tsx`、Nuxt の `nuxt.config` など）を確認する。
- 重篤度の目安: 読み上げが理解できなくなるため Major。

### SEM-10: 一部分の言語

- WCAG: SC 3.1.2 (AA)
- 確認手段: `code`
- 判定: デフォルトの言語以外の言語がページ内に含まれる場合、その部分の要素の `lang` 属性に
  よって言語が機械可読な形で宣言されていること。
  - 判断の方法は SEM-09 と同様である
  - Accessibility Visualizer では「言語」のチップで設定値を表示できる
  - 固有名詞や、その言語に取り込まれている単語は対象外
- 重篤度の目安: 該当箇所が理解できなくなるため Normal。分量が多いなら Major。

### SEM-11: 名前・役割・値

- WCAG: SC 4.1.2 (A)
- 確認手段: `both`
- 判定: HTML 要素、WAI-ARIA のロールや属性は目的に沿ったものを使用し、それらがページの
  目的に沿って正しくスクリーンリーダー等で読み取れる状態になっていること。
  - 名前（アクセシブルネーム）: すべての操作可能な要素が、何をするものかわかる名前を持つ
  - 役割（ロール）: ボタンは `<button>`、リンクは `<a href>` のように、適切な要素または
    `role` で表現されている
  - 値・状態: 開閉状態（`aria-expanded`）、選択状態（`aria-selected` / `aria-current`）、
    チェック状態（`aria-checked`）、無効状態（`disabled` / `aria-disabled`）、
    エラー状態（`aria-invalid`）などが、視覚的な表現と一致して機械可読になっている
- 補足: **これは文法チェッカーや axe-core のようなアクセシビリティチェッカーだけでは検出
  できず、ページの目的に基づいて正しいものが用いられているかを確認しなければならない。**
  スクリーンリーダーでページのすべての部分を読み、すべての部分を操作するのが最も確実だが、
  ソースコードから確認する、Accessibility Visualizer でおかしな部分を探す、という方法で
  代替できる。ただし知識や経験が求められ、見落としも発生しやすい。
  何が「正しい」状態であるかはページの内容や目的に大きく依存するため、この状態で正しいのかを
  製作者に確認しなければならない場合もある。
- `code` からの判定: 以下を探す。
  - `<div>` `<span>` で実装されたボタン・リンク・チェックボックス
  - `role` は付いているが、その role が要求する属性やキーボード操作が実装されていないもの
    （`role="button"` に `tabindex` とキーボードハンドラがない、`role="tab"` に
    `aria-selected` がない、`role="dialog"` にアクセシブルネームがない、など）
  - 開閉するもの（アコーディオン、ドロップダウン、ハンバーガーメニュー）に `aria-expanded`
    がない
  - `aria-labelledby` / `aria-describedby` の参照先 `id` が存在しない、または
    Shadow DOM をまたいでいて解決されない
  - 存在しない role や属性名の誤り（`aria-labeledby` のようなタイポ）
  - `aria-hidden="true"` の中にフォーカス可能な要素がある
- 重篤度の目安: 操作要素の役割や状態が伝わらず操作できないなら Major。
  ページの主要な目的に関わるなら Critical。

### SEM-12: ステータスメッセージ

- WCAG: SC 4.1.3 (AA)
- 確認手段: `both`
- 判定: ステータスメッセージ（アクションの成功もしくは結果、アプリケーションの処理待ち状態、
  プロセスの進捗、エラーの存在に関する情報などを伝える、ページの移動や大幅な変化を伴わない
  コンテンツの変化）が、スクリーンリーダー等の支援技術に通知されること。
  - WAI-ARIA ライブリージョン（`role="status"` `role="alert"` `aria-live`）を使った通知が
    行われることが多い。スクリーンリーダーまたは Accessibility Visualizer で、変化を
    認識できる通知が行われることを確認する
  - スクリーンリーダーによってライブリージョンの挙動はかなり異なる。変化と同時にフォーカス
    移動が発生する場合、ライブリージョンの通知が読み上げられないことがしばしば発生する
- `code` からの判定: 以下を探す。
  - トースト、スナックバー、フォームの送信結果、検索結果件数、バリデーションエラー、
    ローディング表示に、ライブリージョンの指定があるか
  - ライブリージョンの要素が、**通知したい内容が入る前から DOM に存在しているか**
    （後から要素ごと挿入すると通知されないことが多い）
  - `role="alert"` を多用していないか（割り込みが多すぎると使いづらい）
- 重篤度の目安: 操作の成否がわからないため Major。補足的な通知なら Normal。
