フォーム

フォームは情報を「入力させる」場所。a11y の問題が露骨に出やすい。ラベル付けエラー伝達適切な input type、この3つが押さえられているかで品質が決まる。

すべての入力に <label>

スクリーンリーダはフォーカスが入った瞬間に「メールアドレス、必須、テキスト入力」のように ラベル + 状態 + role を読む。<label> がないと「テキスト入力」しか言われない

付け方は2通り

A) for + id(最も推奨)
<label for="email">メールアドレス</label>
<input id="email" type="email" name="email" />
B) ラベルで囲む
<label>
  メールアドレス
  <input type="email" name="email" />
</label>

どちらでも機能する。A を推奨するのは、CSS でレイアウト柔軟性が高いから。 ラベルのクリックでフォーカスが入るのも両方とも同じ。

placeholder はラベルの代わりにならない

最頻出の間違い。placeholder例の入力値を示すもので、ラベルではない。

<!-- BAD -->
<input type="email" placeholder="メールアドレス" />

<!-- GOOD -->
<label for="email">メールアドレス</label>
<input id="email" type="email" placeholder="user@example.com" />

ラジオ・チェックボックスは <fieldset> + <legend>

複数の選択肢が「1つの問い」に紐づく時、共通の見出しが要る。それが <legend>

<fieldset>
  <legend>配送方法</legend>

  <input id="ship-std"     type="radio" name="ship" value="std" />
  <label for="ship-std">通常配送</label>

  <input id="ship-express" type="radio" name="ship" value="exp" />
  <label for="ship-express">速達</label>
</fieldset>

スクリーンリーダは「配送方法、通常配送、ラジオボタン、選択されていません」のように legend を含めて読み上げる。

適切な input type を使う

モバイルでは type に応じてキーボードが切り替わる。a11y であると同時に UX 改善。

type用途キーボード
text汎用テキスト標準
emailメールアドレス@ や . が出やすい
tel電話番号数字キーパッド
number数値(数学的)数字
urlURL/ や . が出やすい
search検索標準 + クリアボタン
passwordパスワード(マスク)標準
date / time / datetime-local日時ピッカー
colorカラーカラーピッカー
fileファイルファイル選択
rangeスライダー左右キー
type="number" の罠

数字なら何でも number にしがちだが、電話番号や郵便番号は telnumber はスクロール/上下キーで値が変わるので、桁数固定の入力には向かない。 ID 系のコードも text + inputmode="numeric" + pattern が安全。

autocomplete 属性

ブラウザの自動入力やパスワードマネージャ、認証関連の機能はこれを見る。常に書く

<input type="text"     name="name"  autocomplete="name" />
<input type="email"    name="email" autocomplete="email" />
<input type="tel"      name="phone" autocomplete="tel" />
<input type="text"     name="zip"   autocomplete="postal-code" />
<input type="password" name="pw"    autocomplete="current-password" />
<input type="password" name="newpw" autocomplete="new-password" />
<input type="text"     name="otp"   autocomplete="one-time-code" />

登録フォームの「名前」と「住所」と「電話番号」を1回入力すれば、次回から全部自動で埋まる。 身体的に入力が大変なユーザーには非常に大きな違い。

必須項目の表現

視覚的な印(*)と機械可読な属性の両方を付ける。

<label for="email">
  メールアドレス <span aria-hidden="true">*</span>
</label>
<input id="email" type="email" required aria-required="true" />

エラーメッセージ

エラーは「どのフィールドの」「何が問題か」「どう直せばいいか」を伝える必要がある。

パターン: フィールド近くに表示する

<label for="email">メールアドレス</label>
<input
  id="email"
  type="email"
  required
  aria-invalid="true"
  aria-describedby="email-error"
/>
<p id="email-error" class="error">
  正しい形式で入力してください(例: user@example.com)
</p>

パターン: フォーム送信時のエラー要約

送信ボタンを押した直後に、エラー一覧をフォーム冒頭に表示してフォーカスを移すと スクリーンリーダ利用者に親切。

<div role="alert" tabindex="-1" id="form-errors">
  <h2>3 件のエラーがあります</h2>
  <ul>
    <li><a href="#email">メールアドレスを入力してください</a></li>
    <li><a href="#age">年齢は数字で入力してください</a></li>
  </ul>
</div>
<script>
  document.getElementById("form-errors").focus()
</script>

パスワード入力

<label for="pw">パスワード</label>
<input id="pw" type="password" autocomplete="current-password" />
<button type="button" aria-pressed="false" aria-label="パスワードを表示">
  <svg aria-hidden="true">...</svg>
</button>

ボタン

カスタムウィジェット(select の代替等)

デザインの都合でネイティブ <select><input type="date"> を捨てると、 そこから先は全部自分で a11y を組み立てる必要がある:

必要なければネイティブを使うのが一番。仕様や見た目の制約でやむを得ない時は ARIA Authoring Practices Guide の実装を参考に。 ライブラリなら react-aria / headless UI / Radix UI の利用が安全。

送信前のバリデーション

具体例: ログインフォーム

<form>
  <h1>ログイン</h1>

  <div role="alert" id="form-error" hidden></div>

  <label for="email">メールアドレス</label>
  <input
    id="email" type="email" name="email"
    autocomplete="email" required
    aria-describedby="email-hint"
  />
  <p id="email-hint" class="hint">登録時のメールを入力</p>

  <label for="pw">パスワード</label>
  <input
    id="pw" type="password" name="password"
    autocomplete="current-password" required
  />

  <label>
    <input type="checkbox" name="remember" />
    ログイン状態を保持
  </label>

  <button type="submit">ログイン</button>
</form>
a11y フォームの最低ライン

✓ 全 input に label が紐づいている
placeholder はラベルの代わりになっていない
✓ ラジオ・チェックボックスのグループに fieldset/legend
✓ 適切な typeautocomplete
✓ 必須は required + 視覚的な印
✓ エラー時に aria-invalid + aria-describedby
✓ 送信ボタンに動詞が入った具体ラベル