shibomb

HTMLフォームをマウスなしで使える形に直す6つの確認項目

見た目はきれいに整っている問い合わせフォームでも、マウスを外した途端に使えなくなることがあります。どの欄にいるのか分からない、選択肢に届かない、エラーが出たのに何が悪いのか読み取れない。こうした問題は、デザインを見ているだけでは気づけません。この記事では、名前・メール・問い合わせ種別・本文だけの小さなフォームを題材に、キーボードだけで点検する手順と、HTMLでの直し方を順に見ていきます。

まずはマウスを置く:Tab・Shift+Tab・Enter・Spaceでフォームを点検する

最初にやることは単純で、マウスから手を離し、キーボードだけでフォームを最後まで送信してみることです。まず使うのは、次のキーです。

  1. Tab:次の操作可能な要素へ進む
  2. Shift+Tab:前の要素へ戻る
  3. Space:チェックボックスの選択を切り替えたり、ボタンを実行したりする
  4. Enter:リンクやボタンを実行する。1行の入力欄ではフォームを送信する場合もあるが、本文用の textarea では通常、改行になる

ラジオボタンは少し違い、グループ内の移動と選択には一般に矢印キーを使います。セレクトボックスも矢印キーで選べますが、開く操作などはOSやブラウザによって異なります。ここを知らないと「Tabで選択肢に届かない」と誤解してしまうので、先に覚えておいてください。WAIのラジオグループの操作説明でも、Tabによるグループへの出入りと、矢印キーによる選択を分けています。

点検中は、次の問いを頭に置きます。いま自分がどこにいるか見えるか。Tabの順番は画面の読み順と合っているか。途中で抜け出せなくなる場所はないか。送信後に何が起きたか分かるか。紙にメモしながら進めると、あとで直す箇所がはっきりします。

正直、最初は気まずいほど多くの問題が見つかるはずです。それで普通です。

入力欄の意味を伝える:labelとidを結び、関連する選択肢をfieldsetでまとめる

キーボード操作とスクリーンリーダーの両方で効くのが、入力欄とラベルの紐づけです。label 使い方の基本は、for 属性と入力欄の id を一致させることです。見た目の文字だけ近くに置いても、プログラムの上では無関係のままです。

<!-- 改善前:文字が横にあるだけ -->
<p>メールアドレス</p>
<input type="email" placeholder="[email protected]">

<!-- 改善後:labelとidを結ぶ -->
<label for="email">メールアドレス</label>
<input type="email" id="email" name="email" autocomplete="email">

結ぶと、スクリーンリーダーが欄の名前として「メールアドレス」を読み上げられるようになります。ラベルの文字をクリックしても入力欄にフォーカスが移るので、マウス利用者にとってもクリック範囲が広がります。label の中に入力欄を入れて関連づける方法もあります。WAIのラベルの解説で、両方の方法が紹介されています。

placeholder はラベルの代わりになりません。入力を始めると消えてしまい、薄い色で読みにくいことも多いためです。WAIのフォームの説明でも、placeholder による説明は常時表示されない点に注意を促しています。

問い合わせ種別のようにラジオボタンが並ぶ場合は、fieldset と legend で質問文ごとまとめます。

<fieldset>
  <legend>お問い合わせの種類</legend>
  <label><input type="radio" name="type" value="service"> サービスについて</label>
  <label><input type="radio" name="type" value="bug"> 不具合の報告</label>
  <label><input type="radio" name="type" value="other"> その他</label>
</fieldset>

質問文と選択肢のグループが関連づけられていないと、読み上げ時に何についての選択肢か分かりにくくなることがあります。legend はグループの名前を伝える役割です。ただし、読み上げ方はスクリーンリーダーや設定によって異なります。WAIのグループ化の解説も、legend が読まれない場合を考慮するよう勧めています。

現在位置を見失わせない:フォーカス表示とHTMLの並び順を整える

キーボード操作で問題になる例の一つが、フォーカスの輪郭が消されていることです。CSSの outline: none で、ブラウザ標準の輪郭線が消える場合があります。デザイン上の理由は分かりますが、個人的には、代わりの表示を用意しないままの outline: none は避けるべきだと思います。現在位置が分からないフォームは、地図なしで知らない街を歩くのと同じです。

input:focus-visible,
select:focus-visible,
textarea:focus-visible,
button:focus-visible {
  outline: 3px solid #1a5fb4;
  outline-offset: 2px;
}

:focus-visible を使うと、フォーカス表示が必要かどうかの判断をブラウザに任せられます。キーボード操作では通常表示されますが、文字入力欄などではマウスでクリックした場合も表示されます。背景との差がはっきり出る色を選んでください。MDNの仕様解説に、ブラウザが判断する際の例が載っています。

もう一つがフォーカス順序です。Tabの順番は、基本的にHTMLに書かれた並び順で決まります。CSSの order やグリッド配置で見た目だけ入れ替えると、画面では「名前→メール」なのにTabでは「メール→名前」になることがあります。対策は単純で、見た目の順にHTMLを書くことです。

tabindex に1以上の数値を指定して順番を操作するのも、おすすめしません。後から項目を足したときに順番が崩れやすく、チームで保守するほど扱いが難しくなります。tabindex="0" と tabindex="-1" 以外は、ほぼ使わなくて済むはずです。

入力エラーを伝える:色に加えて文章を示し、aria-describedbyで補足を関連づける

赤い枠線だけのエラー表示は、色の区別が難しい人や、スクリーンリーダー利用者に伝わりません。WCAG の達成基準 1.4.1 は、視覚的な情報を色だけで伝えないことを求めています。そこで、文章でエラーの内容と直し方を示します。「入力に誤りがあります」では足りません。「メールアドレスに @ が含まれていません。例:[email protected]」のように、実際に検出した問題に合わせて、何をどう直すかまで書きます。

その文章を入力欄に結ぶのが aria-describedby です。ラベルが「何の欄か」を伝えるのに対し、こちらは補足説明を伝えます。

<label for="email">メールアドレス(必須)</label>
<input
  type="email"
  id="email"
  name="email"
  required
  aria-invalid="true"
  aria-describedby="email-hint email-error"
>
<p id="email-hint">返信先として使います。</p>
<p id="email-error">メールアドレスに @ が含まれていません。例:[email protected]</p>

これは、@ がないエラーを検出したあとの表示例です。aria-describedby には、スペース区切りで複数の id を並べられます。欄にフォーカスが入ると補足とエラーも読み上げられるのが一般的ですが、実際の読み上げは環境や設定によって異なります。属性を付けるだけで、後から追加したエラーが即座に通知されるとは限りません。エラーがない状態では aria-invalid を付けず、エラー用の要素ごと出さないようにします。その際は aria-describedby も email-hint だけに戻します。

送信後の動きも設計が必要です。エラーがあるなら、最初のエラー欄へフォーカスを移すか、フォーム上部にエラー一覧を出してそこへフォーカスを送る方法があります。WAIの通知の解説でも、エラー文の関連づけやフォーカス移動が紹介されています。ただ、どちらが良いかはフォームの構成や利用状況によります。項目が4つ程度の小さなフォームなら、最初のエラー欄へ直接移す方法が考えられます。項目が多いなら一覧が親切だと感じます。

改善前後を比較する:問い合わせフォームを直し、チームで使える確認リストにまとめる

ここまでを問い合わせフォームにまとめます。改善前は、ラベルが p 要素、種別の選択肢がグループ化されていない、outline: none が全体指定、エラーが赤枠だけ、という状態を想定します。改善後のHTMLの基本構造は次のようになります。

<form>
  <div>
    <label for="name">お名前(必須)</label>
    <input type="text" id="name" name="name" autocomplete="name" required>
  </div>

  <div>
    <label for="email">メールアドレス(必須)</label>
    <input type="email" id="email" name="email" autocomplete="email"
           required aria-describedby="email-hint">
    <p id="email-hint">返信先として使います。</p>
  </div>

  <fieldset>
    <legend>お問い合わせの種類</legend>
    <label><input type="radio" name="type" value="service"> サービスについて</label>
    <label><input type="radio" name="type" value="bug"> 不具合の報告</label>
    <label><input type="radio" name="type" value="other"> その他</label>
  </fieldset>

  <div>
    <label for="message">お問い合わせ内容(必須)</label>
    <textarea id="message" name="message" rows="6" required></textarea>
  </div>

  <button type="submit">送信する</button>
</form>

この例はブラウザ標準の入力チェックを使います。novalidate を付けると送信時の標準チェックが無効になるため、独自のチェック処理がない例では付けません。MDNのform要素の説明で、この属性の動作を確認できます。先ほどの独自エラー文の表示・更新やフォーカス移動には別途処理が必要で、問い合わせの送信先や受信処理もこの例には含めていません。フォーカス表示には前掲のCSSを組み合わせます。

直したあとは、冒頭と同じようにキーボードだけで最後まで通して確認します。Tabの順番、フォーカスの見え方、エラーが出たときの移動先は、基本的な確認点です。ただし、この3点だけでアクセシビリティ全体の合格を判断できるわけではありません。ラベルやエラーの読み上げ、送信結果が伝わるかも確認します。

チームで使うなら、プルリクエストのレビューに次の確認リストを添える方法が現実的です。

  1. マウスなしで、入力から送信まで完了できる
  2. すべての入力欄に、for と id、または入力欄を囲む方法で関連づけた label がある
  3. 関連する選択肢は fieldset と legend でまとめている
  4. キーボード操作中、フォーカスの表示が見える
  5. Tab順が画面の読み順と一致している
  6. 独自のエラー表示は色以外に文章で示し、aria-describedby で欄と結んでいる

自動チェックツールだけでは、この6項目のうち順序や文章の分かりやすさまでは判定できません。最後は人の手で確認する工程が残ります。最初の一歩は、いま手元にあるフォームを1つ選び、マウスを置いてTabキーを押すことです。

関連記事