表单设计:让用户愿意填完的对话界面
表单是用户与产品"对话"的界面——每一次输入都是用户在回答产品的问题。糟糕的表单会让用户在输入框前犹豫、在错误提示前困惑、在提交按钮前反复确认。这一节从输入框、label 位置、校验反馈、错误状态到提交按钮,把表单设计的每一步讲清楚。
输入框的基础
输入框是表单的原子。一个合格的输入框要有清晰的边框、舒适的内边距、明确的焦点状态。默认状态用奶白背景降低视觉噪音,聚焦时切换为纯白并加上主色光环,让用户一眼知道"现在我在填哪里"。输入框高度建议 44 到 48 像素,既舒适又符合触控规范。
<div class="field">
<label for="email">邮箱地址</label>
<input type="email" id="email" placeholder="you@example.com">
<p class="field-tip">请输入有效邮箱</p>
</div>
.field { margin-bottom: 18px; }
.field label {
display: block;
font-size: 13px;
font-weight: 600;
margin-bottom: 8px;
color: #2A3328;
}
.field input {
width: 100%;
padding: 13px 16px;
border: 1px solid #DFD9CC;
border-radius: 10px;
font-size: 14px;
background: #FBF9F2;
transition: all 0.3s ease;
}
.field input:focus {
outline: none;
border-color: #A8D5BA;
background: #fff;
box-shadow: 0 0 0 3px rgba(127, 176, 105, 0.12);
}
label 的位置
label 放哪里,是个被反复争论的问题。顶置(label 在输入框上方)最稳,扫描顺畅,适合大多数场景;左置适合简短表单,能省垂直空间,但 label 和输入框的对齐容易出问题;浮动 label(placeholder 上浮变成 label)省空间但可读性差,只推荐用在空间极紧的移动端。新手默认选顶置,不会出错。
校验与反馈
校验的时机决定体验。失焦时校验是底线,输入中实时校验更友好,但不要在每个按键都报错——用户还没输完就弹红字,会很挫败。反馈文案要具体:"邮箱格式不正确"比"输入有误"有用得多。成功状态也要给,绿色的勾比一片寂静更让人安心。
.field-tip { font-size: 13px; margin-top: 8px; color: #8A9690; min-height: 18px; }
.field.valid .field-tip { color: #4A7C3A; }
.field.valid input { border-color: #A8D5BA; }
.field.error .field-tip { color: #D26548; }
.field.error input { border-color: #E8826B; background: #FDF2F0; }
错误状态
错误状态不只是把边框变红。要同时做三件事:边框变色、背景轻微染色、下方给出具体原因。颜色只是辅助,文字才是主信息——读屏用户和色盲用户都依赖文字。错误文案要对事不对人:"密码至少 8 位"而不是"您输入有误"。纠错成功后要立刻清除错误样式,别让红字赖着不走,制造不必要的焦虑。
- 边框加背景加文案三重提示,不依赖颜色单一通道。
- 错误文案紧贴输入框下方,不要堆在表单顶部。
- 纠错后立即清除错误状态,别让红字赖着不走。
- 文案对事不对人,说清楚"哪里错了、怎么改"。
提交按钮
提交按钮是表单的终点,也是转化的闸门。它要用 primary 变体,与次级操作(如"取消")拉开层级。提交后立即进入 loading 状态,禁用按钮、显示转圈,防止用户连点造成重复提交。成功后给明确反馈,失败则保留用户已填内容,千万别清空表单让人重填——那是把用户推走的最快方式。
.form-actions { display: flex; gap: 14px; margin-top: 24px; }
.btn-submit { position: relative; /* primary 变体 */ }
.btn-submit.loading { color: transparent; pointer-events: none; }
.btn-submit.loading::after {
content: "";
position: absolute;
width: 18px; height: 18px;
border: 2px solid rgba(255, 255, 255, 0.4);
border-top-color: #fff;
border-radius: 50%;
animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }