# コンポーネントの追跡

チェック対象のファイルから import を辿り、実際にレンダリングされるマークアップの全体像を
把握するための手順。

## 基本方針

アクセシビリティの問題は、**マークアップが最終的にどう組み上がるか**によって決まる。
1つのファイルだけを見ても、以下は判定できない。

- そのコンポーネントが受け取る `children` に何が入るか
- ラップしているコンポーネントが `<button>` を出すのか `<div>` を出すのか
- `id` が正しく `<label for>` と紐付いているか（生成が別ファイルにある場合）
- そのボタンが `aria-expanded` を持つかどうか（共通コンポーネント側の実装次第）

そのため、対象ファイルから import されているコンポーネントを辿る必要がある。

## 追跡の範囲

### 辿るもの

- 自プロジェクト内のコンポーネント（相対パス、および `@/` `~/` `src/` などの
  エイリアスによる import）
- 対象コンポーネントを構成するスタイル定義（CSS Modules、`.css`、`.scss`、
  Tailwind の設定、デザイントークン）
- 対象が使われている側のファイル（呼び出し元）。props に何が渡されるかで結論が変わる観点
  （SEM-01 の代替テキスト、SEM-08 のラベル、SEM-11 のロール）では、呼び出し元の確認が必要になる

### 限定的に辿るもの — 外部パッケージの API コントラクト

UI コンポーネントを npm パッケージで提供する例は多く、`node_modules` をまったく見ないと
判断材料がほとんど得られないことがある。そこで、**`package.json` の `dependencies` に
直接指定されているパッケージに限り**、その **API コントラクトの確認**のために辿ってよい。
ただし用途は次に厳しく限定する。

**目的は「props の契約」を確かめること。** 実装を読んで最終的な DOM を推論することではない。
主に読むのは、そのパッケージの以下である。

- 型定義（`.d.ts`）— どんな props を受け取るか、`alt` / `label` に相当する props が
  **必須か任意か**、`aria-*` を forward するか
- README / JSDoc の注記 — props の意図、アクセシビリティに関する使い方の前提

これで裏を取れるのは「props 次第で結論が変わる観点」（SEM-01 / SEM-03 / SEM-08 など、
後述の表）の**呼び出し側の使い方**である。「このコンポーネントは `label` を必須にしている／
していない」といった設計上の事実は、ここから確実に読める。

**辿らないもの・根拠にしないもの:**

- パッケージの**内部実装（レンダリング結果の DOM）を推論の根拠にしない**。
  `node_modules` の多くはビルド済み・minify 済みで、型や注記が示すのは API の形であって
  出力されるマークアップではない。JSDoc の「accessible」表記は意図であって保証ではない
  （それを根拠に「問題なし」と書くと見逃しに倒れる）
- **推移的依存（`dependencies` の依存の依存）は辿らない。** a11y の実体を内部パッケージに
  委譲するライブラリは多いが（例: `@mui/material`→`@mui/base`、Radix の各コンポーネント→
  `@radix-ui/react-*`）、そこまで追うと深さ・ファイル数の上限を即座に超え、レビュー本体の
  精度を削る。直接依存の API 契約の確認で止める
- ソースを同梱していない（読めるのがビルド成果物だけの）パッケージは、無理に読まず
  「未確認」に回す
- テストコード、Storybook のストーリー（ただし、状態バリエーションの列挙には役立つので、
  存在する場合は状態の把握に読んでよい）

**そして、API 契約を確認したかどうかにかかわらず、ライブラリ起因の観点は引き続き
「実ページでの確認が必要」として扱う。** accessible なライブラリほど `useId`・状態フック・
portal・実行時計算の ARIA を多用し、これは静的追跡では確定できない領域（後述「動的な
マークアップ」）そのものだからである。パッケージ名とバージョンは、これまで通り必ず記録する
（`package.json` を確認する）。バージョンによって実装は変わる。

## 上限

既定の上限は **深さ3・ファイル数50** とする。これを超える場合は、そこで辿るのをやめ、
利用者に範囲を確認する。

```
対象から辿ったコンポーネントが50ファイルを超えました。
以下の範囲に絞ってチェックを進めてよいですか、それとも範囲を分割しますか。
```

範囲が広すぎる状態でチェックを進めると、1つ1つの確認が浅くなり、見落としが増える。
**広く浅く見るよりも、範囲を区切って深く見るほうがよい。**

## 追跡の記録

辿った結果を、以下のような形で利用者に提示してから次の手順に進む。

```
チェック対象のコンポーネントツリー

src/pages/Checkout.tsx
├── src/components/Form/AddressForm.tsx
│   ├── src/components/ui/TextField.tsx
│   └── src/components/ui/Select.tsx
├── src/components/Cart/CartSummary.tsx
│   └── src/components/ui/Table.tsx
└── src/components/ui/Button.tsx

外部ライブラリ（API 契約のみ確認・内部実装と実挙動は未確認）
- @headlessui/react 2.1.2  … Dialog, Listbox
- react-hook-form 7.51.0

スタイル
- tailwind.config.ts
- src/styles/tokens.css
```

外部ライブラリを別枠で示すのは、そこに起因する問題を「内部実装と実挙動は確認していない」と
明示するため。型定義や JSDoc で props の契約まで確認した場合も、その結論（例:「`Dialog` は
`aria-modal` を持つ」）は API の形として読めたことを意味するにすぎず、レンダリング結果や
実行時の挙動を検証したことにはならない。ここは常に実ページ確認へ回す。

## 判定に呼び出し元の確認が必要な観点

以下の観点は、コンポーネント単体では判定できない。呼び出し元を Grep で探す。

| 観点 | 呼び出し元で確認すること |
| --- | --- |
| SEM-01 | `alt` に相当する props に、実際に何が渡されているか |
| SEM-03 | ラベルの props が渡されているか。渡されていない使い方がないか |
| SEM-07 | 見出しレベルの props（`as="h2"` など）が、ページ全体で整合しているか |
| SEM-08 | 表示ラベルと `aria-label` に別の値が渡されていないか |
| SEM-11 | 汎用コンポーネントに、意味に合わない `role` が渡されていないか |
| VIS-18 | リンクコンポーネントに「こちら」のようなテキストが渡されていないか |

汎用的な UI コンポーネント（`Button`、`TextField` など）をチェックする場合、
**その使われ方をすべて確認するのは現実的でないことが多い**。その場合は、コンポーネント自体の
実装の問題と、「この props を渡し忘れるとアクセシビリティの問題が起きる」という設計上の
リスクを指摘し、使用箇所の網羅は範囲外として明記する。

## 動的なマークアップ

以下は静的な追跡では最終的なマークアップを確定できない。「判定不能」として扱い、
実ページでの確認に回す。

- `dangerouslySetInnerHTML` / `v-html` / `{@html}` で挿入される HTML
- CMS やマークダウンから生成されるコンテンツ
- サードパーティのウィジェット（チャット、広告、埋め込みプレイヤー、同意管理バナー）
- サーバーから受け取ったデータによってロールや状態が決まる実装

これらは**アクセシビリティの問題が集中しやすい箇所**でもあるため、「判定不能」で終わらせず、
実ページでの確認が必要な箇所としてレポートに明記する。
