エラーサマリ
送信すると先頭に「3 件の入力に問題があります」が現れてフォーカスが移り、各項目へのリンクで飛べる。項目側は aria-invalid と aria-describedby で理由が読まれる。
プレビュー
操作実際に入力して試せます。
仕様書
- 向く場面
- 送信時にまとめて検証するフォーム
- エラー箇所へリンクで飛ばせたい長いフォーム
- 行政・申込みなど確実に直させる入力
- 避ける場面
- 赤枠だけで伝える
- 「不正な値です」のような原因不明の文言
- 入力中に即時検証したいフォーム
- 構成要素
- エラーサマリ(見出し+リンク一覧)
- 入力欄
- 項目ごとのエラー文
- 同意チェック
- 送信ボタン
- 読み上げ文の帯
- 持っている状態
- 通常
- フォーカス
- エラー
- 成功
- キーボード
Tab で入力欄・チェック・登録へ、Enter で送信。エラー時はサマリ見出しへフォーカスが移り、各リンクを Enter で該当項目へ飛ぶ
- 支援技術
サマリは tabindex=-1 へフォーカス移動、各行は項目へのリンク。項目に aria-invalid=true と aria-describedby でエラー文を結ぶ。成功は role=status。必須は文字「必須」で示す
- 動き
なし
デザインの要点
- エラーはフォームの先頭に集め、見出しへフォーカスを移す。項目ごとの赤字だけでは、読み上げでは何が起きたか分からない
- サマリの各行はその項目へのリンクにする。長いフォームで3つ目のエラーまで自力で辿らせない
- 項目には aria-invalid="true" と、エラー文を指す aria-describedby を付ける。赤枠は読まれない
- エラー文は「何をどう直すか」を書く。「不正な値です」は原因も直し方も伝えない
プロのサイトでの実例
実例は「型の使われ方」の観察であり、コード・ロゴの転載ではありません。サイト側の更新で見え方が変わることがあります。
使いどころ
送信時にまとめて検証する長めのフォーム——申込み、会員登録、行政の手続き——に効く。項目が画面をまたいで並ぶほど、どこで何が起きたかを1か所へ集める価値が上がる。逆に、入力中に1項目ずつ即時検証する設計では送信前にエラーが解消されているので、サマリは空振りしやすい。その場合は項目ごとの説明を厚くし、サマリは「それでも送信できなかったとき」の最後の砦として使う。項目が2〜3個しかない短いフォームでもサマリ自体は害にならないが、フォーカス移動の往復が増えるだけになることがあるので、項目側のエラー表示を強くする方を優先してよい。
仕組み
送信時に全項目を検証し、失敗した項目のラベル・メッセージ・対象 id を集める。1件以上あればサマリ領域に「N 件の入力に問題があります」という見出しと各項目へのリンク一覧を描き、その2つを包むサマリ領域(div・tabindex="-1")へ focus() する。見出しではなくブロック側にフォーカスを当てるのは、見出し→リンク一覧の順にまとめて読ませるため。フォーカスを移すことで、読み上げ利用者にも視覚利用者にも「送信されなかった」「理由はここにある」が同時に伝わる。項目側では aria-invalid="true" を付け、aria-describedby でその項目のエラー文の id を指すので、フォーカスするとラベル→値→エラー文の順に読まれる。検証が通ったときはサマリを消し、成功メッセージを role="status" の領域へ入れて、フォーカスを奪わずに結果だけ伝える。見本は読み上げ文を帯に組み立てて出すので、属性の有無で読まれ方がどう変わるかを追える。
実装の注意
赤枠と赤文字だけで伝える実装が最も多い誤りで、色は読み上げに存在せず、色覚特性によっては視覚的にも判別しづらい。aria-describedby で結ばずにエラー文を隣へ置くだけだと、読み上げは項目とエラーを別々に読むため対応関係が伝わらない。文言も落とし穴で、「不正な値です」は原因も直し方も含まない(見本は「name@example.com の形式で」と期待する形そのものを示している)。サマリへのフォーカス移動を忘れると、画面上部に現れたサマリに気づかないまま同じ送信を繰り返すことになる。逆にエラーが0件のときまでサマリへフォーカスを当てる実装にすると、成功のたびに読み上げ位置が飛ぶので、移動はエラー時だけに限る。
アクセシビリティ
WCAG 3.3.1「エラーの特定」と 3.3.3「エラー修正の提案」に対応する。エラーはテキストで特定でき、かつ直し方が分かる必要がある。必須項目の印は記号だけに頼らず「必須」という語を含めるか、少なくとも凡例で記号の意味を説明する。サマリは見出しとリンク一覧を包むブロックに tabindex="-1" を付けてフォーカス可能にし(見出し自体に付けるのではない)、各行はその項目へのリンクにする(リンクなら読み上げのリンク一覧からも辿れる)。項目の aria-invalid="true" は支援技術へ「この項目は不正」と伝える唯一の機械可読な手段で、赤枠の代替にはならない。成功メッセージは role="status"(aria-live="polite")へ入れ、今読んでいる内容を遮らずに届ける。
調整して使う
調整の余地は3つ。ひとつめは検証のタイミングで、送信時だけにするか、一度エラーになった項目だけ入力中に再検証するかを選ぶ。後者は直したことがすぐ分かるので、長いフォームでは体感が大きく変わる。ふたつめはサマリの文言で、件数を前に出す(「3 件の入力に問題があります」)か、行動を前に出す(「送信できませんでした。3 件を直してください」)かで印象が変わる。みっつめはリンクの飛び先で、入力欄そのものへ飛ばすと即座に直せるが、項目の説明文まで読ませたいときはラベルを含むブロックへ飛ばし、scroll-margin-top を確保して見出しが隠れないようにする。
コード(コピーして使えます)
<form class="fes-form" id="fes-form" novalidate>
<div class="fes-summary" id="fes-summary" tabindex="-1" hidden>
<h2 id="fes-sum-title">3 件の入力に問題があります</h2>
<ul id="fes-sum-list"></ul>
</div>
<div class="fes-field">
<label for="fes-name">お名前 <span class="fes-req">必須</span></label>
<input id="fes-name" type="text" autocomplete="off">
<p class="fes-err" id="fes-name-err" hidden></p>
</div>
<div class="fes-field">
<label for="fes-email">メールアドレス <span class="fes-req">必須</span></label>
<input id="fes-email" type="email" autocomplete="off" placeholder="name@example.com">
<p class="fes-err" id="fes-email-err" hidden></p>
</div>
<div class="fes-field fes-check">
<div class="fes-checkrow"><input id="fes-agree" type="checkbox"><label for="fes-agree">利用規約に同意する</label></div>
<p class="fes-err" id="fes-agree-err" hidden></p>
</div>
<div class="fes-foot">
<button type="submit" class="fes-submit">登録する</button>
<span class="fes-reassure">登録後すぐに使えます。メールは確認用に1通だけ届きます</span>
</div>
<p class="fes-ok" id="fes-ok" role="status" aria-live="polite"></p>
<div class="fes-band"><span class="fes-tag">読み上げ</span><span id="fes-msg">空のまま「登録する」を押すと、サマリ見出しへフォーカスが移ります</span></div>
</form>