form-design

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Form Design

表单设计

Forms are where users give the product data. Every unnecessary obstacle between the user and a completed form is a failure. The design goal is to make correct input easy and incorrect input obvious — before the user submits.

表单是用户向产品提供数据的入口。用户与完成表单之间的任何不必要障碍都是设计失败。表单设计的目标是让正确输入变得容易,错误输入在用户提交前就能被明显识别。

The Three Guidance Layers

三层引导机制

Each layer serves a distinct purpose. Do not collapse them.
每一层都有明确的用途,请勿合并它们。

Layer 1 — Helper Text

第一层 — Helper Text

Explains what to enter. Appears below the input, always visible, in small secondary text.
Email address
[                              ]
Use the email you signed up with.
  • Write in plain language from the user's perspective
  • Keep it to one sentence — if you need more, the field is too complex or misnamed
  • Do not repeat the label ("Enter your email" below a label that says "Email" is redundant)
  • Helper text is not a replacement for a label — the label is still required
说明需要填写的内容。位于输入框下方,始终可见,使用小号次要文本显示。
Email address
[                              ]
Use the email you signed up with.
  • 从用户视角使用平实语言撰写
  • 控制在一句话以内——如果需要更多内容,说明该字段过于复杂或命名不当
  • 不要重复标签内容(在“邮箱”标签下方写“输入你的邮箱”属于冗余)
  • Helper Text不能替代标签——标签仍然是必填项

Layer 2 — Placeholder

第二层 — Placeholder

Shows the format or an example value. Appears inside the input, disappears on typing.
[jane@example.com              ]
  • Use a realistic example, not a description:
    +358 40 123 4567
    not
    Enter phone number
  • Never use placeholder as a label — it disappears and leaves the user without context
  • Keep it grey (
    --color-text-secondary
    ) and lighter than actual input text
  • Optional — not every field needs a placeholder
展示填写格式或示例值。位于输入框内部,用户开始输入后消失。
[jane@example.com              ]
  • 使用真实示例,而非描述性文字:
    +358 40 123 4567
    而非
    Enter phone number
  • 切勿将placeholder用作标签——它会消失,导致用户失去上下文参考
  • 采用灰色(
    --color-text-secondary
    ),颜色比实际输入文本浅
  • 可选字段——并非所有字段都需要placeholder

Layer 3 — Validation

第三层 — Validation

Confirms whether the input is correct. The most important layer.
Email address
[jane@           ] ← invalid
✗ Enter a valid email address.
Validation timing:
  • On blur (leaving the field): default for most fields — validates once the user has finished
  • Real-time (on input): use when the format is complex or the error is likely — password strength, IBAN, VAT number, URL, regex-heavy fields
  • On submit: catches anything missed, scrolls to the first error
Real-time validation must be forgiving at the start — do not show an error the instant the user starts typing. Show it after a short debounce (300–500ms) or after the first character that makes the input definitively wrong.

确认输入内容是否正确。这是最重要的一层。
Email address
[jane@           ] ← invalid
✗ Enter a valid email address.
校验时机:
  • 失焦时(离开字段):大多数字段的默认方式——在用户完成输入后进行校验
  • 实时校验(输入时):适用于格式复杂或容易出错的字段——密码强度、IBAN、增值税号、URL、正则匹配要求高的字段
  • 提交时:捕捉所有遗漏的错误,滚动到第一个错误位置
实时校验在初始阶段必须具备容错性——不要在用户开始输入的瞬间就显示错误。应在短暂延迟(300–500ms)后,或在输入第一个明确导致内容无效的字符后再显示错误。

Submit Button State

提交按钮状态

The submit button enables when the form is valid. This is one of the clearest affordance signals in form design — the user sees the goal and knows when they have reached it.
[Submit]   ← disabled, low contrast, cursor: not-allowed
           (fields incomplete or invalid)

[Submit]   ← enabled, full colour, cursor: pointer
           (all required fields valid)
Implementation:
html
<button type="submit" disabled={!isFormValid}>Submit</button>
For long or complex forms where real-time validation is not practical, do not disable the submit — validate on submit and scroll to errors instead. Disabled submit on a long form frustrates users who cannot tell what is missing.
Loading state on submit: Replace label with spinner, disable the button. Prevent double-submission.

仅当表单验证通过时,提交按钮才启用。这是表单设计中最清晰的功能提示信号之一——用户能看到目标,并且知道自己何时达成目标。
[Submit]   ← disabled, low contrast, cursor: not-allowed
           (fields incomplete or invalid)

[Submit]   ← enabled, full colour, cursor: pointer
           (all required fields valid)
实现方式:
html
<button type="submit" disabled={!isFormValid}>Submit</button>
对于无法实现实时校验的长表单或复杂表单,不要禁用提交按钮——改为在提交时进行校验并滚动到错误位置。长表单禁用提交按钮会让用户因无法得知遗漏内容而感到沮丧。
提交时的加载状态: 将按钮文字替换为加载动画,禁用按钮。防止重复提交。

Field Anatomy

字段结构

[Label]                           [Optional badge if optional]
[Input field                                                  ]
[Helper text — what to enter, format, constraints            ]
[Error message — appears below helper text on validation fail ]
html
<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]                           [Optional badge if optional]
[Input field                                                  ]
[Helper text — what to enter, format, constraints            ]
[Error message — appears below helper text on validation fail ]
html
<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>

Required vs Optional

必填与可选字段

Mark the minority. If most fields are required, mark the optional ones. If most are optional, mark the required ones.
  • Do not rely on colour alone — add a text label ("Required" or asterisk with legend)
  • Place the required/optional indicator in the label, not only in the placeholder or helper text
html
<label>Email <abbr title="Required">*</abbr></label>
<!-- or -->
<label>Phone <span class="badge">Optional</span></label>

标记少数派字段。如果大多数字段为必填,则标记可选字段;如果大多数为可选,则标记必填字段。
  • 不要仅依赖颜色区分——添加文本标签(“必填”或带说明的星号)
  • 将必填/可选标记放在标签中,不要仅放在placeholder或helper text里
html
<label>Email <abbr title="Required">*</abbr></label>
<!-- or -->
<label>Phone <span class="badge">Optional</span></label>

Grouping with Fieldset

使用Fieldset分组

Related fields belong in a
<fieldset>
with a
<legend>
. This is semantic HTML and helps screen readers announce the group context.
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>
Use fieldsets for:
  • Address groups
  • Payment details
  • Radio button groups
  • Checkbox groups

相关字段应放在带有
<legend>
<fieldset>
中。这是语义化HTML,有助于屏幕阅读器播报分组上下文。
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>
Fieldset适用于:
  • 地址组
  • 支付详情
  • 单选按钮组
  • 复选框组

Input Types

输入类型

Use the correct
type
— browsers provide free validation, appropriate keyboards, and autofill.
DataInput type
Email
type="email"
Phone
type="tel"
URL
type="url"
Number
type="number"
Password
type="password"
Date
type="date"
Search
type="search"
Colour
type="color"
On mobile,
type="email"
shows the email keyboard,
type="tel"
shows the numpad. These are free UX improvements.

使用正确的
type
属性——浏览器会提供免费的校验、合适的键盘和自动填充功能。
数据类型输入类型
邮箱
type="email"
电话
type="tel"
网址
type="url"
数字
type="number"
密码
type="password"
日期
type="date"
搜索
type="search"
颜色
type="color"
在移动端,
type="email"
会显示邮箱键盘,
type="tel"
会显示数字键盘。这些都是免费的用户体验优化。

Autofill Support

自动填充支持

Allow browsers to autofill. Do not disable it unless there is a security requirement.
html
<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">    <!-- credit card -->
<input type="password" autocomplete="new-password">
Correct
autocomplete
values reduce friction dramatically for returning users and on mobile.

允许浏览器自动填充。除非有安全要求,否则不要禁用该功能。
html
<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">    <!-- credit card -->
<input type="password" autocomplete="new-password">
正确设置
autocomplete
属性能显著减少回头用户和移动端用户的操作摩擦。

Review Checklist

评审检查清单

  • Every field has a visible label (not just placeholder)
  • Helper text is below the input and explains what to enter
  • Placeholder shows format or example, not a description
  • Validation triggers on blur for simple fields, real-time for complex ones
  • Error message is adjacent to the field that failed
  • Error message is associated via
    aria-describedby
  • Required/optional marked on the minority of fields
  • Submit button is disabled when form is invalid (for short forms)
  • Submit button shows a loading state and prevents re-submission
  • Related fields are grouped in
    <fieldset>
    with
    <legend>
  • Correct
    type
    attribute on all inputs
  • autocomplete
    attributes set on address, contact, and payment fields
  • 每个字段都有可见的标签(不只是placeholder)
  • Helper Text位于输入框下方,说明填写内容
  • Placeholder展示格式或示例,而非描述性文字
  • 简单字段在失焦时触发校验,复杂字段采用实时校验
  • 错误消息紧邻校验失败的字段
  • 通过
    aria-describedby
    关联错误消息
  • 在少数派字段上标记必填/可选
  • 短表单在验证不通过时禁用提交按钮
  • 提交按钮显示加载状态并防止重复提交
  • 相关字段使用带
    <legend>
    <fieldset>
    分组
  • 所有输入框都设置了正确的
    type
    属性
  • 地址、联系信息和支付字段设置了
    autocomplete
    属性