form-design について
デザイナーや開発者向けのフォーム設計ガイドです。入力案内、形式、検証タイミング、送信状態を扱い、正しい入力を促して誤りを見つけやすくします。セマンティックなグループ化、自動入力、多言語文言も取り上げます。
主な機能
- 01ラベル、補助テキスト、プレースホルダーの区別
- 02項目の複雑さに応じた検証タイミング
- 03送信ボタンの有効状態と読み込み状態の管理
- 04適切な入力タイプと自動入力の利用
- 05項目の意味的なグループ化と件数依存文言の処理
活用シーン
- 01分かりやすい登録・購入フォームの設計
- 02複雑な項目のリアルタイム検証設定
- 03エラーと項目の関連付け確認
- 04多言語フォームとテキストテストの対応
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dembrandt/ dembrandt-skills --skill form-designインストール後、Claude Code で/form-designを入力して読み込まれたか確認します。
詳細
フォーム設計
フォームは、ユーザーが製品にデータを入力する場所です。ユーザーがフォームを完成させるまでの間に不要な障害があれば、それは失敗です。設計の目標は、正しい入力を簡単にし、誤った入力に気づきやすくすることです。ユーザーが送信する前に、これを実現しましょう。
3 つのガイダンスレイヤー
各レイヤーには、それぞれ異なる目的があります。統合しないでください。
レイヤー 1 — ヘルパーテキスト
何を入力すればよいかを説明します。入力欄の下に、常に表示される小さな補助テキストとして配置します。
Email address
[ ]
Use the email you signed up with.
- ユーザーの視点に立ち、平易な言葉で書きます
- 1 文に収めます。さらに説明が必要なら、フィールドが複雑すぎるか、名前が適切ではありません
- ラベルの内容を繰り返しません(「メールアドレス」というラベルの下に「メールアドレスを入力してください」と表示するのは冗長です)
- ヘルパーテキストはラベルの代わりにはなりません。ラベルは必須です
レイヤー 2 — プレースホルダー
入力形式や入力例を示します。入力欄の中に表示され、入力を始めると消えます。
[[email protected] ]
- 説明ではなく、現実的な入力例を使います。
Enter phone numberではなく+358 40 123 4567を使います - プレースホルダーをラベルとして使わないでください。入力を始めると消え、ユーザーは文脈を把握できなくなります
- グレー(
--color-text-secondary)にし、実際の入力テキストより薄い色にします - 任意です。すべてのフィールドにプレースホルダーが必要なわけではありません
レイヤー 3 — バリデーション
入力が正しいかどうかを確認します。最も重要なレイヤーです。
Email address
[jane@ ] ← invalid
✗ Enter a valid email address.
バリデーションのタイミング:
- フォーカスが外れたとき(フィールドを離れたとき):ほとんどのフィールドでのデフォルトです。ユーザーが入力を終えてから検証します
- リアルタイム(入力中):形式が複雑な場合や、エラーが起こりやすい場合に使います。パスワードの強度、IBAN、VAT 番号、URL、正規表現を多用するフィールドなどが該当します
- 送信時:見逃したエラーを検出し、最初のエラーまでスクロールします
リアルタイムバリデーションは、入力を始めたばかりの段階では寛容である必要があります。入力を始めた瞬間にエラーを表示しないでください。短いデバウンス時間(300〜500ms)を設けるか、入力が明らかに誤りとなる最初の文字が入力されてから表示します。
送信ボタンの状態
フォームが有効になると、送信ボタンも有効にします。これはフォーム設計における最も明確なアフォーダンスのシグナルの 1 つです。ユーザーは目標を確認し、いつ達成したかが分かります。
[Submit] ← disabled, low contrast, cursor: not-allowed
(fields incomplete or invalid)
[Submit] ← enabled, full colour, cursor: pointer
(all required fields valid)
実装:
<button type="submit" disabled={!isFormValid}>Submit</button>
リアルタイムバリデーションが現実的でない長いフォームや複雑なフォームでは、送信ボタンを無効にしないでください。送信時にバリデーションを行い、代わりにエラーまでスクロールします。長いフォームで送信ボタンが無効になっていると、何が不足しているのか分からないユーザーを苛立たせます。
送信時の読み込み状態: ラベルをスピナーに置き換え、ボタンを無効にします。二重送信を防止します。
フィールドの構成
[Label] [Optional badge if optional]
[Input field ]
[Helper text — what to enter, format, constraints ]
[Error message — appears below helper text on validation fail ]
<div class="field">
<label for="vat">VAT number <span class="optional">Optional</span></label>
<input
id="vat"
type="text"
placeholder="FI12345678"
aria-describedby="vat-helper vat-error"
aria-invalid="true"
>
<p id="vat-helper" class="helper-text">Finnish VAT numbers start with FI followed by 8 digits.</p>
<p id="vat-error" class="error-text" role="alert">Enter a valid Finnish VAT number (e.g. FI12345678).</p>
</div>
必須と任意
少数派のほうを明示します。ほとんどのフィールドが必須なら、任意のフィールドに印を付けます。ほとんどが任意なら、必須のフィールドに印を付けます。
- 色だけに頼らず、テキストラベル(「必須」または凡例付きのアスタリスク)を追加します
- 必須/任意の表示は、プレースホルダーやヘルパーテキストだけでなく、ラベルに配置します
<label>Email <abbr title="Required">*</abbr></label>
<label>Phone <span class="badge">Optional</span></label>
フィールドセットによるグループ化
関連するフィールドは、<fieldset> 内に <legend> とともに配置します。これはセマンティック HTML であり、スクリーンリーダーがグループのコンテキストを読み上げるのに役立ちます。
<fieldset>
<legend>Billing address</legend>
<label>Street</label><input type="text">
<label>City</label><input type="text">
<label>Postal code</label><input type="text">
</fieldset>
次のような場合にフィールドセットを使います:
- 住所のグループ
- 支払い情報
- ラジオボタングループ
- チェックボックスグループ
入力タイプ
適切な type を使います。ブラウザーによるバリデーション、入力内容に応じたキーボード、自動入力を無料で利用できます。
| データ | 入力タイプ |
|---|---|
| メールアドレス | type="email" |
| 電話番号 | type="tel" |
| URL | type="url" |
| 数値 | type="number" |
| パスワード | type="password" |
| 日付 | type="date" |
| 検索 | type="search" |
| 色 | type="color" |
モバイルでは、type="email" を指定するとメール用キーボードが表示され、type="tel" を指定するとテンキーが表示されます。どちらも無料で UX を改善できます。
自動入力への対応
ブラウザーによる自動入力を許可します。セキュリティ上の要件がない限り、無効にしないでください。
<input type="text" autocomplete="name">
<input type="email" autocomplete="email">
<input type="tel" autocomplete="tel">
<input type="text" autocomplete="street-address">
<input type="text" autocomplete="postal-code">
<input type="text" autocomplete="cc-number">
<input type="password" autocomplete="new-password">
適切な autocomplete 値を設定すると、再訪ユーザーやモバイルユーザーの入力の手間を大幅に軽減できます。
数に応じて変わる文言
数によって表現が変わる文字列はすべて、数を名前付きプレースホルダーとしてメッセージカタログを通じて処理します。ビュー内で count === 1 の条件分岐は使わないでください。この三項演算子には、英語に固有の事実が3つハードコードされています。複数形が2種類であること、1を境に分けること、数を名詞の前に置くことです。フィンランド語では名詞が語形変化し、ほかの言語では活用形が3種類以上あったり、数が後ろに置かれたりします。翻訳するまではバグが見えず、翻訳後はすべてのビューに広がります。
UI の文言を翻訳すると、テキストベースのすべてのテストセレクターがロケールに依存するようになります。同じ変更で、テストのセットアップ時にロケールを固定してください。
レビューチェックリスト
- すべてのフィールドに、プレースホルダーだけでなく目に見えるラベルがある
- 補助テキストが入力欄の下にあり、何を入力するかを説明している
- プレースホルダーには説明ではなく、形式または例が示されている
- シンプルなフィールドではフォーカスを外したときに、複雑なフィールドではリアルタイムにバリデーションが実行される
- エラーメッセージが、エラーのあったフィールドの隣に表示される
- エラーメッセージが
aria-describedbyで関連付けられている - 必須/任意の表示が、少数派のフィールドに付けられている
- フォームが無効な場合、送信ボタンが無効になる(短いフォームの場合)
- 送信ボタンに読み込み状態が表示され、再送信が防止される
- 関連するフィールドが
<fieldset>と<legend>でグループ化されている - すべての入力欄に正しい
type属性が指定されている - 住所、連絡先、支払い情報のフィールドに
autocomplete属性が設定されている - 数に応じて変わる文言は、数をプレースホルダーとしてメッセージカタログを通じて処理されている。one/other の三項演算子は使われていない
- テキストで選択するテストではロケールが固定されている