# 分割した欄それぞれに名前がない日付や電話番号の入力欄

Source: https://easeweb.dev/learn/split-fields
Topics: フォーム > 複数の欄に分けた電話番号や日付で、どの欄にも名前がある (WCAG 1.3.1, 3.3.2, 4.1.2)
The fix: すべての欄にそれぞれの名前を付け、全体のラベルを全部の欄に結び付けます。欄をfieldsetで囲んで全体のラベルをlegendにし、各欄に見えるラベルを付けるか、見た目はそのままにrole="group"とaria-labelledbyで欄をまとめ、各欄にaria-labelで名前を付けます。
Test with: keyboard, screen reader, zoom
References: https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html https://www.w3.org/WAI/WCAG22/Understanding/labels-or-instructions.html https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html https://www.w3.org/WAI/WCAG22/Techniques/failures/F86 https://www.w3.org/WAI/WCAG22/Techniques/aria/ARIA17 https://design-system.service.gov.uk/components/date-input/ https://designsystem.digital.gov/components/memorable-date/

よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。

## 場面

オンラインショップの会員登録フォームで、生年月日を尋ねています。「生年月日」というラベルの下に、年・月・日の3つのドロップダウンが斜線をはさんで1行に並びます。どのドロップダウンも、値を選ぶまでは「年」「月」「日」という最初の選択肢を表示しています。1990、05、12を選ぶと、行は「1990 / 05 / 12」になります。最初の選択肢の文字は消え、どれが月の欄でどれが日の欄かを示すものは画面に残りません。年・月・日の順に慣れていれば読めますが、月と日の順は国によって違い、画面にはどちらかを決めるものがありません。スクリーンリーダーでTabキーを押すと、「生年月日、コンボボックス、1990」、次に「コンボボックス、05」、「コンボボックス、12」と聞こえます。2つ目と3つ目の欄には名前がなく、スクリーンリーダーが読む名前も、音声操作で呼ぶ名前もありません。

## 確認できる問題

ラベルは`for="dob-year"`を指定した`label`要素なので、名前が付くのは最初のドロップダウンだけです。labelが名前を付けられるのは1つの欄だけで、3つの欄には付けられません。月と日のドロップダウンにはアクセシブル名がありません。最初の選択肢の「月」はselectの値で、名前ではないため、別の値を選ぶまでしか読まれません。これは、すべてのコントロールに名前を求める4.1.2を満たしません。分割した入力欄の2つ目以降に名前がないこの状態は、W3Cの失敗例F86にそのまま挙がっています。

1.3.1も満たしません。画面では3つの欄が1つの答えであることは明らかで、並び順と斜線がそれぞれの部分を示しています。しかし、その関係はコードのどこにも書かれていません。3つをまとめるグループがないため「生年月日」という言葉は2つ目と3つ目に届かず、この欄が月だと示すものは位置しかありません。

3.3.2も満たしません。3.3.2は、入力するあいだに使えるラベルか説明を求めています。「月」と示すのは最初の選択肢だけで、月を選んだとたんに消えます。文字を入力するとプレースホルダーが消えるのと同じです。そこから先は、全体にはラベルがあっても各欄にはなく、05と12を見分ける手がかりは並び順だけになります。W3Cの3.3.2の解説書も、3つの欄に分けた電話番号を例に、区切りの記号だけでは欄のラベルにならず、「電話番号」という1つのラベルでは3つの欄すべてには名前を付けられないと述べています。

## 利用者への影響

問題が表に出るのは、フォームを埋め終えて見直すときです。日付を直しに戻った人や、送信前にフォームを確認する人は、3つの数字を前に、どの順で選んだかを思い出すか、推測するしかありません。

## 自動チェックとAIの限界

axeの`select-name`ルールとANDIは、名前のない2つのドロップダウンを見つけます。電話番号をテキストボックスに分けた場合も、axeの`label`ルールが同じ誤りを見つけます。しかし、全体のラベルが各欄に届いているかは分かりません。各ドロップダウンに`aria-label`を付ければ、「生年月日」と結び付いていなくても、名前がスクリーンリーダーにしか聞こえなくても、スキャナーは何も報告しなくなります。手早く直そうとして、かえって悪くなることもあります。3つのドロップダウンすべてに`aria-label="生年月日"`を付けてもスキャナーは何も報告しなくなりますが、スクリーンリーダーは「生年月日」を3回読み、どれが月の欄かはやはり分かりません。EaseWebの診断は、名前のある欄がグループに入っていないまとまりや、名前がスクリーンリーダーにしか届かないまとまりを、人が確認する項目（`ew:split-field-manual-check`）として挙げます。耳でも目でも各欄を見分けられるかは、人が確かめるしかないためです。

## 修正前：3つのドロップダウンの上に1つのラベル

```html
<label for="dob-year" class="field-label">生年月日</label>
<div class="date-parts">
  <select id="dob-year" name="dob-year">
    <option value="">年</option>
    <option>2026</option>
    …
  </select>
  /
  <select name="dob-month">
    <option value="">月</option>
    <option>01</option>
    …
  </select>
  /
  <select name="dob-day">
    <option value="">日</option>
    <option>01</option>
    …
  </select>
</div>
```

最初のドロップダウンには「生年月日」という名前があります。問題はそれ以外です。

- 月と日のドロップダウンには名前がなく、スクリーンリーダーは「コンボボックス」と値しか読みません。
- 3つをまとめるものがないため、月や日の欄で「生年月日」は読まれません。
- 「年」「月」「日」の文字は最初の選択肢にしかなく、値を選ぶと消えます。

## 修正後：fieldsetと欄ごとのラベル

```html
<fieldset class="date-parts">
  <legend class="field-label">生年月日</legend>
  <div class="date-part">
    <label for="dob-year">年</label>
    <select id="dob-year" name="dob-year">
      <option value="">年</option>
      <option>2026</option>
      …
    </select>
  </div>
  <div class="date-part">
    <label for="dob-month">月</label>
    <select id="dob-month" name="dob-month">
      <option value="">月</option>
      <option>01</option>
      …
    </select>
  </div>
  <div class="date-part">
    <label for="dob-day">日</label>
    <select id="dob-day" name="dob-day">
      <option value="">日</option>
      <option>01</option>
      …
    </select>
  </div>
</fieldset>
```

1. 行を`fieldset`にし、3つのドロップダウンを1つのグループにします。
2. 「生年月日」をその`legend`、つまりグループの名前にします。フォーカスがグループに入ると、スクリーンリーダーは「生年月日、グループ、年、コンボボックス、1990」のように読みます。
3. 各ドロップダウンに`id`と、見える`label`（年、月、日）をそれぞれ付けます。値を選んだ後も画面に残り、音声操作で「月をクリック」と話せるようになります。
4. 斜線は外します。斜線が示していたことは、ラベルが言葉で示します。

これで修正前の例は直り、1.3.1、3.3.2、4.1.2を満たします。英国政府のGOV.UKの日付入力（Date input）や、米国政府のUS Web Design SystemのMemorable dateと同じ、各欄の上にラベルを置く形です。

## 修正後：見た目はそのままで欄に名前を付ける

```html
<span id="dob-label" class="field-label">生年月日</span>
<div class="date-parts" role="group" aria-labelledby="dob-label">
  <select id="dob-year" name="dob-year" aria-label="年">
    <option value="">年</option>
    <option>2026</option>
    …
  </select>
  /
  <select name="dob-month" aria-label="月">
    <option value="">月</option>
    <option>01</option>
    …
  </select>
  /
  <select name="dob-day" aria-label="日">
    <option value="">日</option>
    <option>01</option>
    …
  </select>
</div>
```

1. `label`を`id`付きの`span`に変え、最初のドロップダウンだけに名前を付けないようにします。
2. 行に`role="group"`を付け、`aria-labelledby`でその`id`を指します。フォーカスが行に入ると、スクリーンリーダーは「生年月日」と読みます。
3. 各ドロップダウンに`aria-label`（年、月、日）を付けます。どの欄にも名前が付き、値と一緒に読まれます。

スクリーンリーダーの利用者にとっては、画面の行を変えずに4.1.2と1.3.1の問題が直ります。W3Cの達成方法ARIA17や、3.3.2の解説書にある電話番号の例と同じ形で、その例も、デザイン上ラベルを表示できない場合に、各欄へ見えない名前を付けています。ただし名前は聞こえるだけで、見えません。同じ解説書は、支援技術にしか届かない名前では、4.1.2は満たしても3.3.2は満たさないことがあると注意しています。3つとも数字が入ると、画面が伝える内容は修正前と変わらないため、3.3.2を満たすのは並びから各欄が明らかな場合に限られ、音声操作の利用者には話すための言葉が画面にありません。すぐには作り直せない行に使い、できるようになったら見えるラベルに移ってください。

## どの方法を選ぶか

どちらもすべての欄に名前を付け、「生年月日」を3つの欄すべてに結び付けます。fieldsetと欄ごとのラベルを使ってください。フォームを埋めた後も、目で見る人にどれがどの欄かを伝えられるのはこちらだけで、しかも素のHTMLだけで済み、隠れた言葉もありません。隠した名前は、今回のリリースではデザインを変えられない場合の一時しのぎです。

## 実装の判断

**並び順はラベルの代わりになりません。** 年・月・日の順は日本の書き方で、多くの国では日・月・年、米国では月・日・年です。欄ごとにラベルがあれば、どの順でも伝わります。ラベルがなければ、順番は推測です。利用者が慣れた順に並べ、そのうえでラベルも付けます。

**最初の選択肢は名前ではありません。** 「年」や「選択してください」のような案内は、空の最初の選択肢として残して構いませんが、それはselectの値で、別の値を選ぶまでしか読まれません。フレームワークもこの形で案内を入れます。DjangoのSelectDateWidgetは、必須でない日付のとき、`empty_label`でこの案内を受け取ります。名前はラベルから付けます。

**フレームワークの補助機能は、各欄に名前を付けません。** Railsの`date_select`は、idが`_1i`、`_2i`、`_3i`で終わる3つのselectを出力するため、属性に対して書いたラベルはどれにも結び付きません。Djangoの標準の`as_div()`レイアウトでは、SelectDateWidgetの3つのselectがfieldsetに入り、フィールドのラベルがlegendになりますが、各selectにはラベルが付きません。`as_p()`、`as_table()`、`as_ul()`のレイアウトでは、ラベルは最初のselectにしか結び付きません。どちらでも、各欄のラベルは自分で付けるか、自前のコンポーネントで欄を描画します。

**電話番号と郵便番号は、1つの欄のほうが向いています。** 市外局番・市内局番・加入者番号に分けた電話番号や、3桁と4桁に分けた郵便番号も、日付と同じく欄ごとに名前が必要です。ただ、よりよい直し方は分けないことです。1つの欄なら、どう入力しても貼り付けても番号を受け取れます。米国政府のUS Web Design Systemは電話番号を複数の欄に分けないよう求め、GOV.UKは利用者が慣れた書き方のまま入力できるよう求めています。入力に合わせて書式を整える欄には別の問題があり、[入力した番号や貼り付けた番号が変わってしまう入力マスク](/ja/learn/input-masks)で扱います。2つのドロップダウンに分けたカードの有効期限には、legendを「有効期限」にしたfieldsetの中で、「月」と「年」のラベルを付けます。

**ブラウザーに入力させます。** 生年月日なら、3つの欄に`autocomplete="bday-year"`、`"bday-month"`、`"bday-day"`を付けると、ブラウザーが保存している値を入力できます。これは別の達成基準で、[自動入力が効かないフォームの入力欄](/ja/learn/autocomplete)で扱います。

**フォーカスは動かしません。** 1つの欄を埋めたとたんに次の欄へフォーカスを移さないでください。GOV.UKの日付入力のパターンも、欄の間を自動で移動させないよう求めています。値を直しに戻る人を驚かせるためです。自動でフォーカスが進む欄の問題は、[キーボードで打ち間違いを直せないワンタイムコードの入力欄](/ja/learn/code-box-keys)で扱います。

**エラーは欄を示します。** 1つの欄が誤っていたら、メッセージでどの欄かを伝え（「月は1から12で入力してください」）、その欄に印を付けます。[直し方がわからないフォームエラー](/ja/learn/accessible-form-errors)を参照してください。カレンダーから選ぶ日付は別のコントロールで、[入力もキーボード操作もできない日付ピッカー](/ja/learn/date-picker)で扱います。

## 修正の確認

1. ブラウザーのアクセシビリティツリーで、各欄にそれぞれの名前（年、月、日）があり、「生年月日」という名前のグループの中にあることを確認する。
2. スクリーンリーダーで、まとまりに上からTabキーで、下からShift + Tabキーで入り、最初に止まる欄で「生年月日」が読まれ、各欄で欄の名前と値が読まれることを確認する。
3. すべての欄で値を選んでから行を見て、どれが年・月・日の欄かが画面上で分かることを確認する。
4. 200%の拡大表示とスマートフォンの幅で、各ラベルが欄のそばに残り、欄が重ならないことを確認する。

OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。共通の日付コンポーネントを変更した際は、再度確認します。

## 制限

このサンプルは、どのアプリケーションでも同じ結果になることを保証するものではありません。スクリーンリーダーはフォーカスがグループに入ったときにグループの名前を読むため、下からShift + Tabキーで戻ると、年の欄ではなく日の欄で読まれます。スクリーンリーダーによっては、欄ごとに繰り返し読むこともあります。このガイドが扱うのは、いくつかの部分に分けて尋ねる日付です。ワンタイムコードのように1文字ずつ欄を分けた値は、[貼り付けられないワンタイムコードの入力欄](/ja/learn/one-time-codes)で扱います。1つの設問の下に並ぶラジオボタンやチェックボックスは、[設問名がないラジオボタンとチェックボックスのグループ](/ja/learn/field-groups)で扱います。実際に使用する支援技術で、フォーム全体を確認してください。

---

このガイドをコーディングエージェントで使う: MCP https://easeweb.dev/mcp（設定は https://easeweb.dev/ja/services/developers）
