小さな画面でも使えるフォームを作る

HTMLBeginner
オンラインで実践に進む

はじめに

Still & Spoke は土曜日に少人数の陶芸ワークショップを開いています。スタジオのページは用意されていますが、申込フォームには見えるラベル、ブラウザー標準の入力検証、条件付きの座席メモ、狭い画面でもキーボードで操作できるレイアウトが必要です。成功メッセージを表示するだけでなく、実際のローカル申込を行います。作品を変更する前に、独立した Study form で完成例を体験します。

基本的な HTML 要素、CSS セレクターとレイアウト、JavaScript のオブジェクト、関数、DOM の選択とイベントを知っている必要があります。この Lab ではラベルの関連付け、フォーム制約、アクセシブルな説明、条件付き required、見えるフォーカスと簡単なレスポンシブなルールを学びます。最初の Lab は用意済みの送信処理とローカル API を利用し、次の Lab でその処理を作ります。人物とアドレスはすべて架空で、メール送信や支払いはありません。実用的な使いやすさの演習であり、完全なアクセシビリティ認証ではありません。50 分は読む時間と通常のミスへの対応を含む著者の見積もりです。

コマンドには Workspace、Web サイトには Studio を使います。編集できるファイルは /home/labex/project/stillspoke/form.html、validity.js、form.css です。他のファイル、用意済みの API、定員ルール、既存の訪問者を保持してください。各 Lab は独立して初期化されます。サーバーは form.html の実際の内容をブランド付きのページ枠に挿入し、不足するラベルを補ったり作品を再生成したりしません。更新後はページの読み込み完了を待ってからフォームを操作してください。文書の言語にかかわらずサイトのラベルは英語です。

各コントロールに見える名前を付ける

このステップでは完成例をキーボードで操作し、作品ページの各コントロールを見えるラベルと説明に関連付けます。

Studio でページ内リンク Study form を選びます。これはサイト内の移動であり、LabEx Tab の切り替えではありません。例には独自の API とデータベースがあり、作品の完成扱いにならず作品の席も消費しません。Your name に見える輪郭が付くまで Tab を押します。Example Guest と入力し、Tab を押して sample@example.test と入力します。Tab で Session に移り、C で Clay, slowly / 10:00 を選び、選択されたタイトルを読んでから進みます。この標準選択ボックスは選択肢の先頭文字の入力に対応します。Tab で支援チェックボックス、Tab で任意メモ、Tab で Reserve my place に移ります。Enter を一度押します。Submission result に Reserved for Example Guest とサーバーが発行した Confirmation SS-… が出るまで待ちます。受動的な送信メッセージは消えます。結果領域にフォーカスが移るので完了結果を見つけやすくなります。

下図は独自の見出しと本物の確認番号を持つ結果です。発行される番号は異なります。

独立した完成例での実際の確認

例のページで Studio records を選び、同じ名前、アドレス、読みやすい時刻、確認番号を探します。このページが読むのは受付済みレコードであり、ブラウザーの結果文ではありません。Back to signup、続いて Work form を選び、自分のページに戻ります。作品はまだ未完成で、新しい作品用申込もありません。完成例の結果は数えません。

Workspace に切り替えます。cd はこのターミナルの作業ディレクトリを変更します。絶対パスは / から始まります。pwd はそのディレクトリを表示します。次のコマンドで編集先を明確にします。

cd /home/labex/project/stillspoke
pwd

想定される出力:

/home/labex/project/stillspoke

フォームの name 値は API のキー name、email、session、support、note です。HTML の id はページの一要素を識別します。ラベルの for は入力欄の id を指すため、見えるラベルのクリックや支援技術による読み上げで対象を識別できます。ここで Your name は for="person" を使いますが、送信キーは name です。役割が異なります。チェックボックスをラベルの内側に置くと暗黙の関連ができます。autocomplete="name" と autocomplete="email" はブラウザーが補完候補を出せる情報の種類を示すだけで、送信はしません。入力中に消えるプレースホルダーは見えるラベルの代わりになりません。

required はリクエスト送信前に空値を拒否します。type="email" は不正なメール構文を拒否するようブラウザーに求めますが、所有者は証明しません。minlength="2" は名前に 2 文字以上を要求します。先頭の空の選択肢が実際の回の選択を必須にします。選択肢の value はサーバー上の安定した識別子、見えるタイトルは人向けです。位置からその関係を推測しないでください。

aria-describedby は空白区切りの ID を含み、各フィールドを常時表示のヘルプと最初は空のエラー段落に結びます。ヘルプは失敗前にルールを説明し、エラーはサーバー拒否後に理由を追加します。色だけでなく明確な文章も使います。aria-hidden="true" は装飾の矢印だけを支援技術から隠します。↗ は HTML の数値文字参照で、ASCII のソースがブラウザーでは矢印になります。Sending の段落は受動的通知用に role="status" を持ちます。結果領域の aria-labelledby は見出し ID を参照し、その領域に名前を与えます。最初の hidden は表示する内容ができるまで空の結果を隠します。独立した結果領域の tabindex="-1" は、通常の Tab 順序に停止箇所を増やさずスクリプトからフォーカスできるようにします。状態通知自体にはフォーカスしません。

次のコマンドが置き換えるのは form.html だけです。cat は続くテキストを書き出し、> は指定ファイルを上書きします。<< の後の引用符付き HTML は here-document の開始で、単独行の HTML までがそのままファイル内容になります。引用符は shell が HTML 内の文字を解釈するのを防ぎます。終端マーカーまで全体を貼り付けてください。shell のプロンプトが戻れば保存完了で、別の保存キーはありません。Markdown のバッククォートは貼り付けません。後の JavaScript と CSS にも同じ方法を使います。

cd /home/labex/project/stillspoke
cat > form.html <<'HTML'
<form id="signup">
  <div class="pair">
    <div class="field">
      <label for="person">Your name (required)</label>
      <input id="person" name="name" required minlength="2" autocomplete="name"
             aria-describedby="name-help name-error">
      <small id="name-help">At least two characters.</small>
      <p class="error" id="name-error"></p>
    </div>
    <div class="field">
      <label for="email">Email (required)</label>
      <input id="email" name="email" type="email" required autocomplete="email"
             aria-describedby="email-help email-error">
      <small id="email-help">Practice address: you@example.test</small>
      <p class="error" id="email-error"></p>
    </div>
  </div>
  <div class="field">
    <label for="session">Session (required)</label>
    <select id="session" name="session" required aria-describedby="session-help session-error">
      <option value="">Choose a time</option>
      <option value="clay-morning">Clay, slowly / 10:00</option>
      <option value="clay-afternoon">Clay, slowly / 14:00</option>
    </select>
    <small id="session-help">Choose a listed time. The afternoon session is full.</small>
    <p class="error" id="session-error"></p>
  </div>
  <label class="check"><input type="checkbox" name="support"> I need seating assistance</label>
  <div class="field">
    <label for="note">Seating note (required when assistance is selected)</label>
    <textarea id="note" name="note" rows="2" aria-describedby="note-help note-error"></textarea>
    <small id="note-help">When assistance is selected, describe the help in at least 5 characters.</small>
    <p class="error" id="note-error"></p>
  </div>
  <button type="submit">Reserve my place <span aria-hidden="true">&#8599;</span></button>
  <p id="progress" role="status"></p>
  <section id="result" aria-labelledby="result-heading" tabindex="-1" hidden>
    <h3 id="result-heading">Submission result</h3>
    <p id="message"></p>
  </section>
</form>
HTML

Studio に戻り作品ページを更新します。入力後もラベルを読めることを確認します。Email をクリックして入力欄へのフォーカス移動を見てから、Tab で文書順に進みます。空のまま Reserve my place を実行すると、ブラウザーは Your name にフォーカスして必須と説明するはずです。1 文字入力して再試行し、長さのルールを観察します。Morgan Vale のような名前を使い、not-an-email で異なるメール形式拒否を見ます。これは標準検証メッセージであり HTTP 422 ではありません。ブラウザーはまだ申込リクエストを送っていません。修正時は対象欄にフォーカスし Ctrl+A または Cmd+A で文字を選択して置き換えます。タイプミスで VM をリセットする必要はありません。

現時点では支援を選んでもメモは任意です。次にこの振る舞いを追加します。狭い画面用のスタイルもまだありません。自己確認:5 つすべてに見える関連ラベルがあり、ヘルプが見え続け、必須値が空なら送信できないこと。

座席メモをチェックボックスに連動させる

このステップでは座席支援を求めるときだけメモを必須にし、入力済みの内容を保持します。

チェックボックスのブール値 checked は選択時 true、それ以外は false です。入力欄やテキストエリアの required は HTML 制約に対応するプロパティです。STUDIO は scenario.js が読み込む用意済みの設定オブジェクトで、この回の noteMin は 5 です。今後のすべての回が同じだと決めつけず、この数値を読みます。設定は場面のデータを提供するもので、条件ルールそのものは実装しません。

querySelector は CSS セレクターに合う最初の要素を返します。[name="support"] と [name="note"] はその属性に一致します。API の name で対象を選びます。関数はチェック変更のたびに 2 つの標準制約を更新します。condition ? first : second の条件演算子は真なら first、それ以外は second を選びます。そのため支援時の最小長は 5、非支援時は 0 です。一度 updateNote() を呼んで読み込み時の状態も初期化します。change リスナーは切り替え後に再実行します。変更するのは制約であり .value ではないので、選択解除しても下書きは消えません。

Workspace で validity.js を置き換えます。JS マーカーは前の HTML と同じ役割です。

cd /home/labex/project/stillspoke
cat > validity.js <<'JS'
const support = document.querySelector('[name="support"]');
const note = document.querySelector('[name="note"]');
function updateNote() {
  note.required = support.checked;
  note.minLength = support.checked ? STUDIO.noteMin : 0;
}
support.addEventListener('change', updateNote);
updateNote();
JS

Studio に戻り更新します。Tab で 2 文字以上の名前、morgan@example.test のような構文が正しいアドレス、Clay, slowly / 10:00 を入力・選択します。I need seating assistance 上で Space を押して選びます。メモを空にして Reserve my place を実行します。今度はブラウザーが Seating note にフォーカスして入力を求めるはずです。near と入力し、Tab で離れて再送信を試します。4 文字は必要な 5 文字に足りません。minlength はユーザー入力に適用されるため、この演習ではスクリプト代入ではなく普通にタイプします。

必要に応じ Shift+Tab でチェックボックスへ戻り Space で解除します。メモは任意になり near は残ります。再び支援を選び、メモを A seat near the entrance にします。次のステップのため入力を残してください。更新は未送信の内容を捨てますが、LabEx Tab を切り替えるだけならこのページは開いたままです。

チェックが効かないようなら Workspace に戻り、実際に保存したファイルを表示します。

cd /home/labex/project/stillspoke
cat validity.js

上の短いコードと比較し、checked の綴りと末尾の updateNote(); も確認します。必要なら全体を置き換えて Studio を更新します。スクリプト欠落や JavaScript のタイプミスは、ブラウザーによる空メモの拒否とは違います。申込がなかっただけで標準制約が動いたと判断してはいけません。自己確認:チェックで必須状態が変わり、選択時だけ短いメモを拒否し、既存のメモを保持すること。

読みやすさと見えるフォーカスを保つ

レイアウトを仕上げ、作品ページで実際に申し込み、対応する受付済みレコードを読みます。

2 列のフォームは折りたためる必要があります。CSS Grid の 1fr 1fr は名前とメールに等しい幅を与え、min-width: 0 は縮小を許します。幅が最大 800 ピクセルのとき、メディアクエリーはこの組を 1 列にします。width: 100% と box-sizing: border-box は余白と枠線を使用可能幅の内側に含めます。input:not([type="checkbox"]) はチェックボックスを全幅ルールから外し、小さな四角のままにします。文章はホバーなしで読めます。ページ枠は既に小画面でストーリーをフォームの上に積むので、これらのルールはその中にフォームを収めます。

:focus-visible はキーボードフォーカスに見える輪郭を付けます。色だけが手掛かりではなく、必須の文言、エラーの言葉、輪郭の形も重要です。明確で同等な枠線や影も有効ですが、すべてのフォーカス表示をなくすのは不可です。[aria-invalid="true"] はサーバーが無効とした欄のスタイルです。.error:empty が隠すのは空のエラー段落だけで、ラベルや必要な説明ではありません。overflow-wrap: anywhere は長い確認番号や架空アドレスの折り返しを可能にします。動きを減らす設定は、アニメーションを減らしたい人に滑らかな移動を追加しないためのものです。このフォームにアニメーションは不要です。

Workspace で form.css を置き換えます。

cd /home/labex/project/stillspoke
cat > form.css <<'CSS'
form { margin-top: 28px; }
.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.field { min-width: 0; margin-bottom: 18px; }
label { display: block; margin-bottom: 7px; font-size: 13px; font-weight: bold; }
input:not([type="checkbox"]), select, textarea {
  width: 100%; box-sizing: border-box; padding: 11px 12px;
  border: 1px solid #89988a; border-radius: 3px;
  font: inherit; font-size: 15px; color: #243d35; background: #fcfaf5;
}
small { display: block; margin-top: 6px; font-size: 11px; color: #56645a; }
.check { display: flex; gap: 9px; align-items: center; margin: 20px 0; }
.check input { width: 18px; height: 18px; accent-color: #244c3d; }
button {
  padding: 14px 20px; border: 0; border-radius: 3px;
  font: inherit; font-size: 14px; color: #fffdf6; background: #254f41; cursor: pointer;
}
button[type="submit"] { width: 100%; display: flex; justify-content: space-between; }
button:disabled { background: #5b6c5e; cursor: wait; }
:focus-visible { outline: 3px solid #ad4d28; outline-offset: 4px; }
[aria-invalid="true"] { border: 2px solid #9d3327; }
.error { color: #963024; font-size: 12px; margin: 5px 0; }
.error:empty { display: none; }
#progress, #message { font-size: 14px; overflow-wrap: anywhere; }
#result { margin-top: 20px; border-top: 1px solid #c9cebf; padding-top: 12px; }
#result h3 { font-size: 15px; margin: 0 0 8px; }
#records article { border-top: 1px solid #c9cebf; overflow-wrap: anywhere; padding: 12px 0; }
@media (max-width: 800px) { .pair { grid-template-columns: 1fr; gap: 0; } }
@media (prefers-reduced-motion: reduce) { * { scroll-behavior: auto !important; } }
CSS

Studio に戻り更新して作品フォームを使います。Morgan Vale、morgan@example.test と入力し、C で Clay, slowly / 10:00、Space で支援を選び、A seat near the entrance と入力します。Tab で送信ボタンに移り Enter を一度押します。用意済み処理の本物の確認を待ちます。この最初の Lab は処理を提供しており、あなたはその周囲の意味付け、制約、スタイルを実装しました。次の Lab でリクエストとフィードバックのロジックを説明し、作成します。

Studio records を選び、同じ名前、メール、時刻、番号を見つけます。Existing Visitor と Avery Reed を残します。これらは新しい申込と独立したレコードで、席を空けるために消すのは範囲外です。定員を強制するのはサーバーであり、無効なフロントエンドボタンではありません。続いて Back to signup を選びます。更新で未送信欄が消えても、保存済み申込はこの Lab 内に残ります。

ブラウザーまたは Studio パネルを約 390 ピクセルに狭め、約 768 ピクセルのタブレット幅も見ます。横スクロールがなく、ラベルとヘルプは見え続け、名前/メールは縦に並ぶべきです。Tab ですべてをたどり、フォーカスされた各コントロールの表示を確認します。広い幅に戻し 2 列になることも確認します。収めにくい欄を隠して解決してはいけません。

古いレイアウトのままなら、/home/labex/project/stillspoke の form.css に書き、base.css ではないことを確認して更新します。パス間違いに VM リセットは不要です。自己確認:狭い画面と広い画面で全体を読んで操作でき、実際の作品申込が記録され、既存訪問者が残ること。検証は実データを読み、変更しないファイルのコピーを一時的な隔離データで実行します。あなたの代わりに実フォームを送信しません。

まとめ

実際のコントロールに見えるラベル、標準制約、ヘルプを関連付け、入力を失わない条件付きメモと、見えるキーボードフォーカスを持つレスポンシブレイアウトを作りました。独立した例を使ってから本物の作品申込を行い、受付済みレコードと確認番号を照合しました。次の独立した Lab で送信とフィードバック処理を作ります。