Part 3 Webアプリケーション開発

第12章 フロントエンドとアクセシビリティ

第11章では、支援ステータス機能をHTTP JSON APIとして設計した。 第12章では、そのAPI契約を、利用者が実際に触れる画面へ変換する。

フロントエンドは、APIのresponseを画面に貼る作業ではない。 利用者が何を見て、何を判断し、どの操作を行い、今何が起きているのかを理解できるようにする仕事である。 同じAPIでも、画面状態、フォーム、エラー文、フォーカス、キーボード操作が整っているかどうかで、使いやすさは大きく変わる。

この章では、担当受講者の支援状況画面を題材にする。 画面要件、UI state、画面構造、フロントエンド実装方針、ブラウザとアクセシビリティの確認ログまでを一続きで扱う。 特定のフレームワークの細かい書き方は扱わない。 まずは、API契約を利用者の操作と状態へ翻訳できることを目標にする。

この章でできるようになること

この章を読み終えた時点で、次のことを自分の言葉で説明できる状態を目指す。

  • 第11章のAPI契約を、画面に表示する情報、操作、状態へ変換できる。
  • API field、画面に見せる文言、画面状態として持つ値を分けられる。
  • loadingempty、filtered-empty、load-error、saving、saved、save-errorを区別して設計できる。
  • セマンティックHTMLを使い、見出し、フォーム、ラベル、ボタン、表の意味を説明できる。
  • ARIAを足す前に、まずHTMLの標準要素で表せないかを確認できる。
  • エラー表示を、色だけでなく言葉、位置、入力欄との関係で設計できる。
  • キーボード操作、フォーカス、画面幅、コントラストの基本確認を自分で行える。
  • NetworkConsoleElementsを使い、見た目だけでなく通信、例外、HTML構造を確認できる。
  • スターターアプリの最小実装と、実務で求める画面品質の差分を説明できる。
  • AIが生成したUIを、API契約、既存デザイン、アクセシビリティ確認に照らして検証できる。

フロントエンドは、利用者の行動を成立させる場所である

バックエンドAPIが正しくても、利用者に価値が届くとは限らない。 画面で何を見ればよいか分からない。 保存中なのか止まっているのか分からない。 エラーが出ても何を直せばよいか分からない。 キーボードでは操作できない。 このような画面では、機能として動いていても、利用者の仕事は進みにくい。

支援ステータス機能で、メンターがやりたいことは単純である。 担当受講者の状態を見たい。 要支援の人を見つけたい。 必要なら支援ステータスやメモを更新したい。 保存できたか、失敗したかを知りたい。

フロントエンドは、この流れを画面として成立させる。 データ取得、表示、絞り込み、入力、保存、エラー、確認を、利用者が理解できる形にする。

API契約から画面要件へ変換する

第11章で設計したAPIは、画面要件の材料になる。

GET /api/mentor/learners
GET /api/mentor/learners?supportStatus=needs_support
PATCH /api/mentor/learners/{learnerId}/support-status

このAPIから、画面に必要なことを取り出す。

  • 担当受講者一覧を表示する。
  • 支援ステータスで絞り込める。
  • 受講者ごとに支援ステータスと支援メモを更新できる。
  • 保存中、保存成功、保存失敗が分かる。
  • 担当受講者が0件の場合の表示がある。
  • 絞り込み結果が0件の場合の表示がある。

API fieldをそのまま並べるだけでは、画面要件として足りない。 namesupportStatussupportNoteupdatedAt はresponse bodyの項目である。 画面要件では、それぞれが利用者の判断にどう役立つかを書く。

たとえば、受講者名は誰の状態かを見分けるために表示する。 支援ステータスは、次に声をかけるべき受講者を判断するために表示する。 支援メモは、前回の対応内容や注意点を思い出すために表示する。 更新日時は、情報が古くないかを判断するために表示する。

画面には出さないが、操作に必要な情報もある。 learnerId は、画面上で大きく表示する必要はないかもしれない。 しかし、更新APIのpath paramsに使うため、画面状態として持つ必要がある。 見せる情報と、操作のために持つ情報を分けることが、画面要件の第一歩である。

API契約から画面要件へ変換するときは、次のように見る。

API契約の要素 画面要件で考えること 支援ステータス画面の例
endpoint どの操作で呼ぶか 初回表示と絞り込みで一覧取得APIを呼ぶ
response field 利用者の判断にどう役立つか updatedAt は情報の新しさを判断するために表示する
path params 画面には出さなくても操作で必要か learnerId は更新APIのURLに使う
query params UI上のどの入力で変えるか supportStatus は絞り込みselectで変える
error response 利用者に何を伝えるか FORBIDDEN は権限がないことを伝える
認可 画面で隠すだけでよいか 担当外更新は画面で隠しても、API側で必ず拒否する

画面で操作できないようにすることと、APIで拒否することは別である。 たとえば、担当外受講者の保存ボタンを画面に出さないとしても、requestを直接送ればAPIは呼べる。 そのため、第11章で設計した認可は必ずAPI側に残す。

screen-requirements.mdに書くこと

screen-requirements.md は、実装前に画面の目的と範囲をそろえる文書である。 screen-requirements.md には、画面名、利用者、目的、表示する情報、画面には出さないが操作に使う情報、操作、画面状態、対象外、迷っていることを書く。

支援ステータス画面なら、次のように考える。

  • 画面名:担当受講者の支援状況。
  • 利用者:ログイン中のメンター。
  • 目的:担当受講者の支援状態を見て、必要な対応へ進む。
  • 表示する情報:受講者名、支援ステータス、支援メモ、更新日時。
  • 操作に使う情報:受講者ID、現在の絞り込み条件、編集中の値。
  • 操作:絞り込み、支援ステータス変更、メモ更新、保存。
  • 対象外:自動アラート、分析ダッシュボード、CSV出力。

この文書の目的は、画面の完成図をきれいに描くことではない。 何を表示し、何を操作し、どの状態を扱うかを、実装前に説明できるようにすることである。

API値を利用者向けの表示へ変換する

APIの値は、画面でそのまま表示するとは限らない。 たとえば、APIでは needs_support という値が返る。 これはプログラムにとって扱いやすい内部値である。 しかし利用者には、「要支援」と表示したほうが分かりやすい。

支援ステータスは、内部値、表示名、補足説明を分ける。

type SupportStatus = "none" | "needs_support" | "in_progress" | "resolved";

const SUPPORT_STATUS_LABELS: Record<SupportStatus, { label: string; description: string }> = {
  none: { label: "未設定", description: "まだ支援ステータスが設定されていません。" },
  needs_support: { label: "要支援", description: "メンターの確認が必要です。" },
  in_progress: { label: "支援中", description: "対応中の受講者です。" },
  resolved: { label: "解決済み", description: "支援対応が完了しています。" },
};

このように分けると、APIとの通信では内部値を使い、画面では表示名を使える。 テストやレビューでも、どの値がどの表示に変換されるかを確認しやすい。

ただし、変換は画面だけの都合で勝手に変えてよいものではない。 第9章の用語、第11章のAPI契約、利用者が普段使う言葉と合わせる必要がある。 「未対応」「支援不要」「未設定」が混ざると、メンターの判断がぶれる。 表示名も小さな仕様である。

表示変換は、次のように表で残すとレビューしやすい。

API値 UI表示 説明 見た目の手がかり 並び順
needs_support 要支援 メンターの確認が必要 テキスト、アイコン、強調色 1
in_progress 支援中 対応中 テキスト、状態ラベル 2
resolved 解決済み 対応完了 テキスト、状態ラベル 3
none 未設定 まだ設定されていない テキスト 4

色を使う場合も、色だけを意味にしない。 赤っぽい表示だけで「要支援」を表すのではなく、「要支援」というテキストを必ず出す。 並び順も仕様である。 メンターが優先度の高い受講者を見つける画面なら、needs_support を先に置くなど、利用者の判断に合う順番を選ぶ。

UI stateは、利用者が今の状況を理解するための設計である

画面は、いつも正常にデータを表示しているわけではない。 最初に読み込んでいる時間がある。 一覧が0件のことがある。 絞り込み結果が0件のこともある。 API取得に失敗することがある。 保存中の時間がある。 保存が成功することも、失敗することもある。

これらを区別しないと、利用者は不安になる。 読み込み中なのに空に見える。 保存中なのに何度もボタンを押せる。 権限エラーなのに入力ミスのように見える。 このような状態の混同は、実装のバグだけではなく、設計不足として起きる。

ui-state-model.md では、一覧の状態と更新フォームの状態を分ける。

一覧の状態:

  • loading:初回読み込み中。
  • loaded:一覧取得に成功し、受講者が表示されている。
  • empty:担当受講者が0件。
  • filtered-empty:絞り込み結果が0件。
  • load-error:一覧取得に失敗した。

更新フォームの状態:

  • idle:まだ変更していない。
  • editing:入力中。
  • saving:保存中。
  • saved:保存成功。
  • save-error:保存失敗。

状態を分けると、画面に何を表示し、どの操作を許すかを決めやすくなる。 たとえば、saving では保存ボタンを無効にし、二重送信を防ぐ。 save-error では、何が起きたかと、再試行できるかを表示する。 filtered-empty では、受講者が存在しないのではなく、現在の絞り込み条件に合う受講者がいないことを伝える。

状態は、表示、操作、次の遷移まで書く。

状態 画面に表示すること 操作できること 次に起きること
loading 読み込み中であること 必要なら再読み込みは抑制 成功で loaded、失敗で load-error
empty 担当受講者がまだいないこと 再読み込み、別画面への導線 データ追加後に loaded
filtered-empty 条件に合う受講者がいないこと 絞り込み解除、条件変更 条件変更で loading
load-error 取得に失敗したこと、再試行できること 再試行 再試行で loading
editing 入力中の値 保存、取り消し 保存で saving
saving 保存中であること 二重送信を防ぐため保存操作は無効 成功で saved、失敗で save-error
save-error 何が失敗したか、次にできること 入力を直す、再試行 再保存で saving

保存失敗時に、入力中の値を消してはいけないことが多い。 利用者はエラーを直して再試行したいからである。 APIから最新値を再取得する場合でも、編集中の値を破棄するのか、確認を出すのかを決める。 「保存できませんでした」だけで入力が消えると、利用者は同じ作業をやり直すことになる。

APIエラーとブラウザ側の失敗を分ける

第11章では、APIのerror responseを設計した。 第12章では、それを利用者に見える文言へ変換する。

たとえば、APIの INVALID_SUPPORT_STATUS は、画面では「支援ステータスを選択してください」や「指定できない支援ステータスです」と表示できる。 FORBIDDEN は、「この受講者の支援ステータスを変更する権限がありません」と表示できる。 UNAUTHENTICATED は、ログイン状態が切れている可能性を伝える必要がある。

一方で、ブラウザ側の失敗もある。 ネットワークが切れている。 サーバーに接続できない。 JavaScriptの例外で処理が止まっている。 これらは、APIが意図して返したerror responseとは違う。

画面では、APIエラーと通信失敗を混ぜない。 利用者に見せる文言と、開発者が調査するためのログを分ける。 利用者には、次にできることを示す。 開発者は、Network、Console、サーバーログを見る。

アクセシビリティは、標準と実際の操作の両方で見る

アクセシビリティは、雰囲気や善意だけで確認するものではない。 W3CのWCAG 2.2は、Webコンテンツをアクセシブルにするための国際的な標準である。 WCAG 2.2は、知覚可能、操作可能、理解可能、堅牢という四つの原則に整理され、テスト可能な達成基準を持つ。

ただし、この章でWCAGの全項目を覚える必要はない。 最初に見るべきなのは、日々の実装で頻出する基本である。

  • 情報がテキストや構造として伝わるか。
  • キーボードだけで主要な操作ができるか。
  • フォーカス位置が見えるか。
  • エラーが言葉で分かり、入力欄と結びついているか。
  • 色だけで意味を伝えていないか。
  • HTMLの意味が、画面の見た目と合っているか。

ARIAは Accessible Rich Internet Applications の略で、HTMLだけでは表しにくい意味や状態を補う属性群である。 しかし、ARIAを足せばアクセシブルになるわけではない。 まず buttonselectlabeltableform などのネイティブHTMLで表せるかを見る。 ネイティブHTMLで表せるものを、div role="button" や独自キーボード操作で作り直すと、かえって壊れやすい。 ARIAは、足りない意味を補うために必要な範囲で使う。

セマンティックHTMLは、意味で要素を選ぶことである

HTMLは、見た目を作るためだけの記号ではない。 画面の意味をブラウザ、支援技術、テスト、他の開発者へ伝える構造である。

ページの主見出しなら h1 を使う。 絞り込み条件なら labelselect を使う。 入力をまとめるなら form を使う。 操作なら button を使う。 複数の受講者を行と列で比較するなら table が自然な場合がある。 単なる見た目のために、クリックできる div を増やすと、キーボード操作や読み上げ、テストが難しくなる。

最小の構造は、次のように書ける。

<section aria-labelledby="support-heading">
  <h1 id="support-heading">担当受講者の支援状況</h1>

  <form aria-describedby="support-filter-help">
    <label for="support-filter">支援ステータスで絞り込む</label>
    <p id="support-filter-help">表示する受講者を支援ステータスで絞り込みます。</p>
    <select id="support-filter" name="supportStatus">
      <option value="">すべて</option>
      <option value="none">未設定</option>
      <option value="needs_support">要支援</option>
      <option value="in_progress">支援中</option>
      <option value="resolved">解決済み</option>
    </select>
    <button type="submit">絞り込む</button>
  </form>
</section>

属性名を暗記することが目的ではない。 入力欄にラベルがあるか。 説明と入力欄が結びついているか。 buttonの文言が操作内容を表しているか。 見出しから画面の目的が分かるか。 まずこの基本を見る。

一覧をtableで表すなら、表の目的と列の意味も伝える。

<table>
  <caption>担当受講者の支援状況</caption>
  <thead>
    <tr>
      <th scope="col">受講者</th>
      <th scope="col">支援ステータス</th>
      <th scope="col">支援メモ</th>
      <th scope="col">操作</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Aoi Tanaka</td>
      <td>要支援</td>
      <td>提出が止まっているため確認する</td>
      <td>
        <label for="support-status-l-101">Aoi Tanakaの支援ステータス</label>
        <select id="support-status-l-101" name="supportStatus">
          <option value="none">未設定</option>
          <option value="needs_support" selected>要支援</option>
          <option value="in_progress">支援中</option>
          <option value="resolved">解決済み</option>
        </select>
        <button type="button">Aoi Tanakaの支援ステータスを保存</button>
      </td>
    </tr>
  </tbody>
</table>

同じ「保存」ボタンが各行に並ぶ場合、ボタン名がすべて同じだと、支援技術やテストで区別しにくい。 画面上は短く見せたい場合でも、行の文脈と組み合わせてどの受講者の操作か分かるようにする。 aria-label で補う方法もあるが、まずは見えるテキストや近くのラベルで分かる形を検討する。

スターターアプリは、学習用に画面とAPIの流れを小さく見せる最小例である。 そのため、英語表示、行ごとのボタン名、保存中状態、エラーと入力欄の関連づけなどは、実務でそのまま十分とは限らない。 課題では、スターターを観察しながら、どこを画面要件やアクセシビリティ方針として補うかを書く。

一覧、フォーム、エラー表示を設計する

担当受講者の支援状況は、複数の受講者を比較する画面である。 受講者名、支援ステータス、支援メモ、更新日時、操作を横に並べるなら、tableが合う場合がある。 一方、スマートフォン幅で縦に積むなら、カード状のリストが読みやすい場合もある。

選択の基準は見た目だけではない。 行と列で比較する情報ならtable。 一人ずつ詳細を読む情報ならlist。 どちらを選んだか、screen-structure.md に理由を書く。

フォームでは、入力欄だけでなく、ラベル、説明、エラー、保存ボタン、保存中の状態をセットで考える。 支援ステータスを選ぶ select には、「支援ステータス」のラベルを付ける。 支援メモの textarea には、「支援メモ」のラベルを付ける。 任意項目なら任意だと分かる説明を付ける。 エラーがあるなら、どの入力欄に関係するかが分かる場所に置く。

悪いエラー表示は、赤い枠だけで終わることである。 良いエラー表示は、何が問題で、どう直せばよいかを言葉で伝える。

  • 悪い例:エラーです。
  • 良い例:支援ステータスを選択してください。
  • 良い例:支援メモは500文字以内で入力してください。

色は手がかりの一つである。 しかし、色だけを唯一の情報にしない。 テキスト、アイコン、位置、見出し、補足文を組み合わせる。

入力欄と説明、エラーの関係はHTML上でも結びつける。

<label for="support-note-l-101">支援メモ</label>
<p id="support-note-help-l-101">500文字以内で入力してください。</p>
<textarea
  id="support-note-l-101"
  name="supportNote"
  aria-describedby="support-note-help-l-101 support-note-error-l-101"
  aria-invalid="true"
></textarea>
<p id="support-note-error-l-101">支援メモは500文字以内で入力してください。</p>

aria-describedby は、入力欄と補足説明やエラー文を関連づけるために使える。 aria-invalid="true" は、その入力値が不正であることを示す。 ただし、属性を付けるだけで十分ではない。 利用者が読める位置にエラー文を置き、何を直せばよいかを具体的に書く。

保存成功や保存失敗のような画面全体のメッセージは、role="status"aria-live を使うと、支援技術へ変化を伝えやすくなる場合がある。 ただし、すべての文言を強制的に読み上げるようにすると、かえって邪魔になる。 重要度の高いエラーと、軽い状態更新を分けて考える。

キーボード操作とフォーカスを確認する

アクセシビリティは、専門家だけが最後に確認するものではない。 実装者がまず見るべき基本がある。 Tabキーで自然な順番に移動できるか。 EnterやSpaceでbuttonを操作できるか。 selectやtextareaにたどり着けるか。 フォーカスが見えるか。 保存後やエラー後に、利用者が次に見る場所が分かるか。

フォーカスは、今どこを操作しているかを示す印である。 フォーカス表示を消すと、キーボード利用者は現在位置を見失う。 保存ボタンを押した後、画面の上部に成功メッセージを出すなら、利用者がそのメッセージに気づけるかも考える。 エラーが出たら、エラー文の近く、または該当入力欄へ戻る方針を決める。

キーボード確認では、次を実際に試す。

確認 見ること
Tab順 見た目の流れと大きくずれていないか
Shift+Tab 逆方向にも自然に戻れるか
Enter / Space buttonを操作できるか
select / textarea キーボードだけで値を変更できるか
保存中 無効化されたbuttonに意味があり、二重送信できないか
エラー後 該当入力欄やエラー文へ戻れるか、迷わないか

フォーカス順は、基本的にHTMLのDOM順で決まる。 見た目だけCSSで大きく並べ替えると、目で見る順番とTab順がずれることがある。 tabindex の正の値で無理に順番を作ると、保守が難しくなる。 まずDOM順とHTML構造を自然にし、フォーカス表示を消さない。

この章でWCAGを網羅的に学ぶ必要はない。 ただし、WCAGが示す、知覚可能、操作可能、理解可能、堅牢という大きな考え方は、日常の確認にも使える。 見えるか。 操作できるか。 理解できるか。 壊れにくい構造か。 この四つの問いに置き換えると、画面確認の入口になる。

CSSは、情報の優先度と状態を伝える

CSSは、装飾だけのものではない。 余白、配置、文字サイズ、境界線、色、状態表示を使って、情報のまとまりと優先度を伝える。

支援ステータス画面では、受講者名、現在の支援ステータス、メモ、更新日時、保存操作の優先度を考える。 要支援の受講者を見つけることが目的なら、支援ステータスの表示は見つけやすくする必要がある。 ただし、目立たせるために色だけへ依存してはいけない。 「要支援」というテキストや補足も一緒に出す。

画面幅も確認する。 デスクトップでは横並びで見える表が、モバイル幅では文字が切れるかもしれない。 保存ボタンが画面外へ出るかもしれない。 主要な情報と操作が崩れないかを、幅を変えて見る。 高度なレスポンシブ設計までは扱わないが、主要導線が壊れていないことは必ず確認する。

文字と背景のコントラストも見る。 WCAG 2.2のAA基準では、通常サイズのテキストは少なくとも4.5:1、大きいテキストは少なくとも3:1のコントラスト比が目安になる。 ボタンの境界、入力欄の枠、フォーカス表示のようなUI部品も、背景と区別できる必要がある。 正確な判定にはブラウザ拡張や開発者ツールを使うとよい。

レスポンシブ確認では、単に「小さくしても見える」だけでは足りない。

  • 横スクロールが必要な表なら、スクロールできることが分かるか。
  • 受講者名、状態、保存操作が画面幅で分断されすぎないか。
  • ボタンやselectが指やキーボードで操作しにくくなっていないか。
  • 長いメモや長い受講者名でレイアウトが崩れないか。
  • 拡大表示やブラウザの文字サイズ変更で、主要操作が消えないか。

フロントエンド実装方針を書く

frontend-implementation-note.md は、コードを全部貼る場所ではない。 どのAPIを呼び、responseをどう画面状態へ変換し、どの部品に分け、保存とエラーをどう扱うかを説明するメモである。

書くことは、次のように分ける。

  • API接続:一覧取得、絞り込み、支援ステータス更新。
  • データ変換supportStatus の内部値を表示名へ変換する。
  • 画面状態:一覧データ、絞り込み条件、編集中の値、保存中かどうか、エラー。
  • コンポーネント分割:ページ、フィルタ、一覧、フォーム、エラー表示。
  • 保存処理:入力値を読む、保存中にする、APIへ送る、成功または失敗を反映する。
  • エラー処理:API error responseと通信失敗を利用者向け文言へ変換する。
  • AIを使う場合の検証:採用前に何をブラウザで確認するか。

たとえば、コンポーネント分割は次の程度でよい。

LearnerSupportPage
  -> SupportStatusFilter
  -> LearnerSupportTable
  -> SupportStatusForm
  -> ErrorMessage

分ける目的は、細かい部品を増やすことではない。 データ取得、絞り込み、一覧表示、入力、保存、エラー表示の責務を読みやすくすることである。 小さすぎる分割は、かえって読む場所を増やす。 変更理由と確認範囲を説明しやすい単位を選ぶ。

保存処理では、二重送信と結果の反映を見る

保存処理は、ボタンを押したらAPIを呼ぶだけではない。 利用者が押した瞬間から、結果が返るまでの間を扱う必要がある。

基本の流れは次のように考えられる。

1. 入力値を読む。
2. 入力値を画面側で軽く検証する。
3. 保存中の状態にする。
4. 保存ボタンを一時的に無効にする。
5. PATCH APIへ送る。
6. 成功したら一覧や該当行を更新する。
7. 失敗したらエラー文を表示し、再試行できる状態に戻す。

画面側の検証は、利用者を早く助けるために行う。 ただし、画面側で検証したからAPI側の検証が不要になるわけではない。 requestは書き換えられる。 最終的な入力検証と認可はAPI側でも必ず行う。

保存中に二重送信できると、同じ更新が重複したり、後から返ったresponseで画面が古い状態に戻ったりすることがある。 保存中はボタンを無効にする、文言を「保存中」に変える、保存が終わるまで再送信できないようにする。 これは見た目の工夫ではなく、状態を安定させるための設計である。

保存結果の反映には、いくつかの案がある。

内容 向いている場合 注意点
responseを使って該当行だけ更新 PATCHのsuccess responseで画面状態を更新する 更新結果がresponseに十分入っている response fieldと画面状態の対応をそろえる
保存後に一覧を再取得 PATCH成功後にGETで最新一覧を取り直す 他の項目も同時に変わる可能性がある 通信回数が増え、読み込み状態の扱いが必要
楽観的更新 API結果を待つ前に画面を先に更新する 失敗が少なく、即時反映が重要 失敗時に元へ戻す処理が必要

初学者は、まずresponseを使って該当行を更新する案か、保存後に一覧を再取得する案から考えるとよい。 楽観的更新は便利だが、失敗時の巻き戻し、同時更新、古いresponseの扱いが難しくなる。 使う場合は、失敗時にどの状態へ戻すかを frontend-implementation-note.md に書く。

fetch は、サーバーから 400500 が返っても、それだけではJavaScript例外にならない。 response.ok を確認し、error responseを読んで画面のエラー文に変換する必要がある。 一方、ネットワーク切断やCORSなどでresponse自体を受け取れない場合は、catch 側で通信失敗として扱う。

ブラウザで、見た目以外も確認する

画面確認は、目で見て終わりではない。 ブラウザの開発者ツールで、少なくともNetwork、Console、Elementsを見る。

Network: どのAPIへrequestを送ったか、method、URL、status code、request body、response bodyを確認する。 絞り込みで supportStatus=needs_support が送られているか。 更新で PATCH /api/mentor/learners/{learnerId}/support-status が呼ばれているか。 エラー時に、想定したerror responseが返っているかを見る。

Console: JavaScriptのエラーや警告がないかを見る。 画面は表示されていても、内部で例外が出ていることがある。 エラーがあるなら、操作、時刻、メッセージ、stackの概要を記録する。

Elements: HTML構造を見る。 label と入力欄が結びついているか。 button が操作として存在しているか。 見出し構造が極端に飛んでいないか。 エラー文が入力欄の近くにあるか。

Accessibility: ブラウザによっては、Elementsの中でアクセシビリティツリーやcomputed accessibility propertiesを確認できる。 ここでは、入力欄やbuttonの名前が利用者に分かるか、状態や説明が伝わるかを見る。 たとえば、行ごとの保存ボタンがすべて「Update」とだけ読まれるなら、どの受講者の操作か分かりにくい。

表示条件: 可能なら、画面幅、文字サイズ、ズーム、OSの強制色や高コントラスト設定でも主要操作が崩れないかを見る。 すべての環境を網羅する必要はない。 ただし、通常幅だけ、マウスだけ、見た目だけで確認済みにしない。

frontend-check-log.md には、見た感想ではなく、見た事実を書く。 API request、HTTP status、response、Console error、Elementsで見たlabel、キーボード操作の結果を残す。

frontend-check-logは、品質を説明する証拠である

frontend-check-log.md は、PRや第13章のテスト設計で再利用する。 成功画面だけでなく、失敗時とアクセシビリティの基本確認を含める。

最低限、次を確認する。

  • 一覧が表示される。
  • 絞り込みが動く。
  • 支援ステータスを更新できる。
  • 保存中、保存成功、保存失敗が分かる。
  • Networkでrequestとresponseを確認した。
  • Consoleに重大なエラーがない。
  • Elementsでlabel、form、buttonの関係を確認した。
  • Tabキーで主要な操作へ移動できる。
  • EnterまたはSpaceでbuttonを操作できる。
  • フォーカスの位置が見える。
  • 行ごとのselectやbuttonが、どの受講者の操作か分かる。
  • エラーがテキストで分かる。
  • エラーが入力欄や操作と近く、関係が分かる。
  • 色だけで状態を伝えていない。
  • テキストと背景、フォーカス表示、状態ラベルのコントラストに大きな問題がない。
  • desktopとmobile幅で主要な情報と操作が崩れていない。

確認できなかったことも書く。 たとえば、実際のスクリーンリーダーでは確認できていない、モバイル実機では未確認、権限エラーのテストデータがない、などである。 残る不安を隠さないほうが、レビューで次の確認につなげやすい。

AIは、画面生成だけでなく確認観点にも使う

AIは、画面構成、状態の洗い出し、コンポーネント分割、フォーム実装案、エラー表示案、アクセシビリティ確認観点、ブラウザ確認手順の初稿に使える。 ただし、AIが出した画面をそのまま採用してはいけない。

AIへ渡す前提は、具体的にする。 第11章のAPI契約、支援ステータスの値、扱う画面状態、対象外、既存デザインの制約、確認したいアクセシビリティ観点を渡す。 実データ、個人情報、認証情報、内部URLは渡さない。

採用前に確認することは、次である。

  • API契約にない項目や操作が混ざっていないか。
  • supportStatus の内部値と表示名が正しく対応しているか。
  • label、button、form、tableまたはlistの意味が合っているか。
  • 保存中に二重送信できないか。
  • エラーが色だけでなく言葉でも伝わるか。
  • Tab、Enter、Spaceで主要な操作ができるか。
  • Network、Console、Elementsで確認した結果を説明できるか。

AIが生成したUIで起きやすい問題も知っておく。

  • divspan にクリック処理を付け、buttonとしての意味やキーボード操作を失う。
  • 入力欄にplaceholderだけを置き、labelを付けない。
  • エラーを赤い枠や色だけで表し、テキストで説明しない。
  • 保存中でもボタンを押せて、二重送信できる。
  • API契約にないfieldやendpointを勝手に追加する。
  • 画面上の表示名とAPIへ送る内部値がずれる。
  • 横幅が狭い画面で、表やボタンが重なる。
  • フォーカス表示をCSSで消してしまう。

AI案は、実装の候補である。 画面の意味、API契約との整合、アクセシビリティ確認、ブラウザでの事実確認は、人が責任を持つ。

画面状態と操作性で確認すること

screen-requirements.md、ui-state-model.md、screen-structure.md、frontend-implementation-note.md、frontend-check-log.md を作る。

screen-requirements.md には、画面名、利用者、目的、表示する情報、画面には出さないが操作に使う情報、操作、画面状態、対象外、迷っていることを書く。

ui-state-model.md には、loading、loaded、empty、filtered-empty、load-error、idle、editing、saving、saved、save-errorを分けて書く。 API error responseと、通信失敗などブラウザ側の失敗も分ける。

screen-structure.md には、見出し、一覧、フォーム、ラベル、説明、エラー表示、button、キーボード操作、フォーカス、色だけに依存しない工夫を書く。

frontend-implementation-note.md には、API接続、表示変換、画面状態の持ち方、コンポーネント分割、保存処理、エラー処理、AI案の検証を書く。

frontend-check-log.md には、画面表示、API接続、Network、Console、Elements、キーボード操作、ラベルとエラー表示、色だけに依存していないか、画面幅、見直すこと、確認できなかったことを書く。

成果物の目的は、見た目の完成度を自慢することではない。 利用者が目的を達成できるか、画面状態が抜けていないか、アクセシビリティの基本を確認したかを説明できるようにすることである。 この章で扱う確認は、アクセシビリティ監査の完全な代替ではない。 ただし、見出し、ラベル、button、キーボード操作、フォーカス、エラー、色、コントラスト、画面幅の基本を自分で確認してからレビューへ出すことはできる。 未確認の支援技術や実機がある場合は、確認できていないこととして残す。

画面設計で起きやすい誤解

  • API responseの項目をそのまま並べれば画面要件になると考える。
  • learnerId のような操作に必要な値を、画面状態として持つことを忘れる。
  • 正常表示だけを作り、loading、empty、error、saving、savedを後回しにする。
  • 担当受講者0件と、絞り込み結果0件を同じ表示にする。
  • 支援ステータスの内部値を、そのまま利用者に見せる。
  • クリックできる div を増やし、buttonやformの意味を失う。
  • ラベルなしの入力欄や、色だけのエラー表示を作る。
  • placeholderがあるからlabelは不要だと思う。
  • ARIAを足せば、HTMLの意味が壊れていてもアクセシブルになると思う。
  • 行ごとの保存ボタンがすべて同じ名前でも問題ないと思う。
  • マウス操作だけ確認し、キーボード操作とフォーカスを見ない。
  • フォーカス表示を見た目の都合で消す。
  • コントラストや文字サイズ変更を確認しない。
  • ConsoleやNetworkを見ず、画面が表示されたことだけで確認済みにする。
  • スターターアプリの最小UIを、実務でそのまま十分なアクセシビリティ品質だと思う。
  • AIが生成したUIを、API契約、既存デザイン、アクセシビリティ確認と照合せずに採用する。

フロントエンドとアクセシビリティの章で持ち帰ること

第12章で身につけるべきことは、API契約を利用者の画面体験へ変換することである。 フロントエンドは、APIの結果を貼るだけの場所ではない。 表示、操作、画面状態、入力、保存、エラー、フォーカス、キーボード操作を通じて、利用者の行動を成立させる場所である。

画面要件では、表示する情報と、表示しないが操作に使う情報を分ける。 UI stateでは、loading、empty、filtered-empty、load-error、saving、saved、save-errorを分ける。 HTMLは、見た目ではなく意味で選ぶ。 エラーは、色だけでなく言葉で伝える。 ブラウザ確認では、Network、Console、Elements、キーボード操作、画面幅を見る。

テストとコード品質の章へ

次章では、この画面とAPIが変更後も壊れていないことを、テストとコード品質の観点から説明する。 第12章で作ったui-state-model.mdとfrontend-check-log.mdは、第13章でテスト観点へ変換する。 画面状態、エラー、フォーカス、手動確認に残すこと、自動テストにすることを分けて考える。

参考資料

教材を検索