ラベルがない・表示と食い違うフォームの入力欄
配送先フォームはラベル付きに見えますが、一つはただのテキスト、一つは違うIDを指すlabel、一つは別の言葉のaria-labelで、見えている言葉が欄の名前になっていません。
問題
どの欄にもラベルがあるように見えるのに、スクリーンリーダーや音声操作の利用者には、どの欄が何か分かりません。
花屋の購入手続きで、配送先を入力します。「お名前」「郵便番号」「電話番号」の三つの欄があり、それぞれの上に太字で項目名があります。よくある整ったフォームに見え、マウスを使う人は問題なく入力できます。しかしスクリーンリーダーでTabキーを押すと、一つ目は「編集テキスト」、二つ目も「編集テキスト」、三つ目は「連絡先、編集テキスト」と読み上げられます。音声操作で「電話番号をクリック」と言っても反応しません。画面上には、その兆候がまったくありません。
「お名前」と「電話番号」はdiv要素です。欄の近くにありますが、コード上の結び付きがないため、一つ目の欄には名前がありません。見た目の関係がマークアップにないので、1.3.1を満たしません。「郵便番号」は本物のlabelですが、for="post-code"が欄のid="postcode"と一致しないため、何のラベルにもなっていません。これも1.3.1を満たさず、欄には名前がありません。電話番号の欄はaria-label="連絡先"が見えている「電話番号」を置き換えています。2.5.3は、見えている言葉が名前に含まれ、見たとおりに話せば操作できることを求めています。
スクリーンリーダーの利用者には、欄の種類しか伝わらないか、画面と違う名前が聞こえます。音声操作の利用者は見えているラベルを言っても欄が反応しません。ラベルをクリックして欄を選べる、広い操作領域も失われます。「お名前」や「郵便番号」をクリックしても何も起きません。
目視では問題が見つかりません。どの欄にも項目名があるからです。axeのlabelルールは最初の二つを検出しますが、aria-labelを名前と数えるため、電話番号の欄は表示と違っても合格します。AIによる手早い修正は、すべての欄にaria-labelを足しがちです。警告は消えても、誤ったforはそのままで、名前が画面の文字と違うこともあります。名前と見える言葉が一致しているかは、人が見比べて確かめます。
試してみる
各ラベルをクリックしてから、Tabキーで入力欄を移動してください。
見た目はどちらも同じです。ラベルをクリックすると、その欄に入れますか?フォームの下で、見えるラベルとスクリーンリーダーが読む名前を比べてください。
- 画面のラベル
- —
- スクリーンリーダーの読み上げ
- (Tabで入力欄へ移動)
再現デモです。「問題あり」は意図的にアクセシブルでない状態にしています。
修正方法
見えている言葉を欄の名前にします。label要素か、見えるテキストへのaria-labelledbyを使います。
- 1すべての欄に、値が入っている間も表示されるラベルの文言がある。
- 2その見える文言を、label要素かaria-labelledbyで欄のアクセシブルな名前にする。
- 3見えるラベルと違う言葉のaria-labelを付けない。
<div class="field-title">お名前</div><input name="name" type="text"> <label for="post-code" class="field-title">郵便番号</label><input id="postcode" name="postcode" type="text"> <div class="field-title">電話番号</div><input name="phone" type="tel" aria-label="連絡先">この方法を選ぶ理由:まずHTMLの要素を使う
見た目は修正後と同じです。しかし一つ目は名前がなく、二つ目のラベルは存在しないIDを指し、三つ目は画面が「電話番号」なのに「連絡先」という名前になっています。
forとidで各labelを欄に結び付けると、書かれた言葉がそのまま名前になります。スクリーンリーダーは「お名前、編集テキスト」と読み、音声操作は「電話番号をクリック」に反応し、ラベルをクリックすると欄にフォーカスが移ります。郵便番号のforはidと一致し、aria-labelを削除したので、見える文字を上書きするものもありません。
どちらも見える文言を欄の名前にし、1.3.1、2.5.3、3.3.2を満たします。label要素を使ってください。素のHTMLで同じことができ、さらにラベルのクリックで欄にフォーカスが移ります。aria-labelledbyではこれができません。aria-labelledbyは、外部のテンプレートなど構造を変えられない場合に使い、変えられるようになったらlabelに置き換えます。
プレースホルダーはヒントで、ラベルではありません。 見える項目名をプレースホルダーに置き換えるのも、ラベルを失うよくある原因です。値が入ると消えるため、3.3.2を満たしません。「100-0001」のような入力例は、本物のラベルと併用する場合にだけ使います。灰色のプレースホルダーは、ラベルに必要なコントラストも不足しがちです。
囲む書き方でも構いません。 <label>電話番号 <input type="tel"></label>なら、forとidなしで関連付けられます。一つ目の方法と同じ修正で、IDを生成できないコンポーネントに向いています。古い音声操作ソフトではforとidの方が確実な場合があります。
名前はラベルの言葉から始めます。 スクリーンリーダー向けに補足が必要なら、aria-describedbyや見えるラベルの後ろの隠しテキストで足し、名前が見える言葉で始まるようにします。
コンポーネント。 共通の入力欄コンポーネントでラベルを必須にし、idとforをコンポーネント内で一緒に生成します。例のように一文字違う手書きのforは、レビューでは見つかりません。
修正の確認
マウスを使わない4つの確認
確認方法: キーボード、スクリーンリーダー、ズーム、強制配色
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。共通の入力欄コンポーネントを変更した際は、再度確認します。
制限
このサンプルは、どのアプリケーションでも同じ結果になることを保証するものではありません。ラジオボタンのようなグループにはfieldsetとlegendも必要ですが、このガイドでは扱いません。外部ウィジェット、決済用のiframe、コンテンツ編集者が追加した欄も同じように確認してください。実際に使用する支援技術で、操作全体を確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
コードを更新しても、使いやすさを保つために
ガイドの内容を、コーディングエージェントやCIで使える開発ルールにまとめます。更新後は、キーボードとスクリーンリーダーで再テストし、問題が再発していないか確認します。