输入框最佳实践Input Field Best Practices
离开输入框时自动校验
先看结论
输入框是用户在应用中最常用的组件之一。从「类型选择」(text vs email vs password)到「状态反馈」(焦点、错误、禁用),每个细节都影响用户体验和数据质量。这个词条不讲"怎么做"而讲"为什么这样做"。
- 适合
- 任何需要用户输入的地方——登陆、注册、搜索、表单、编辑。关键是要根据输入内容的性质选择合适的 type,而不是所有输入都用 `type="text"`。
- 不适合
- 不要用输入框让用户在有限选项中选择(用 Select 或 Radio);不要用输入框做可以通过其他方式收集的信息。
直接发给 agent
Create a complete form input field for [type of input].
Requirements:
- Input type: [text/email/password/number] (based on data type)
- Label: [field name] with required indicator (*)
- Placeholder: [example value or hint]
- Helper text: [explain what this field is for]
- Height: 44px (mobile touch target)
- States: normal, focus, filled, error, disabled
- Validation:
* Timing: [real-time on blur / on submit]
* Error message position: below input
* Error color: #A02C2C (error from design system)
- Accessibility:
* <label for="[id]"> correctly associated
* aria-invalid="true" + aria-describedby when error
* Visible focus ring
Output:
- HTML structure with semantic markup
- CSS for all states (including focus-visible)
- Validation logic (show/hide error message)
- Mobile responsive considerations
已于 2026-07-20 实测通过
技术标注5 项
- 输入框高度
- 移动端 ≥44px(触达区标准);桌面 40px 可接受
- 焦点态
- 必须有可见 focus ring(用户按 Tab 时能看到)
- 标签关联
- <label for='email'> 与 <input id='email'> 关联(屏幕阅读器需要)
- 占位文案
- placeholder 不能替代 label;必须同时有 label
- 验证时机
- 实时验证(Blur)vs 提交时验证,根据情景选择
详细说明
输入框最佳实践
它是什么
输入框是用户和应用通话的地方。点击它、敲键盘、收到反馈——这个循环中的每一步都能提升或破坏用户体验。
一个看似“简单”的输入框其实包含 10+ 个设计决策:
- 用什么
type(text vs email vs password) - 高度多少(44px 触达区 vs 其他)
- 焦点时怎么突出
- 验证错误怎么显示
- 移动端键盘怎么处理
- 可访问性标记怎么加
这个词条不是“怎么写 HTML”,而是“为什么这样设计”。
技术标注
| 考虑点 | 决策 | 原因 |
|---|---|---|
| 高度 | 移动端 ≥44px | iOS/Android 标准触达区,防止误触 |
| 焦点环 | 2px Cinnabar outline | 用户按 Tab 时能清晰看到焦点位置 |
| 标签 | <label for="id"> |
屏幕阅读器可以读出字段含义;点标签也能激活输入框 |
| 验证时机 | Blur 触发 | 用户离开字段时检查,不会在打字时骚扰 |
| 错误位置 | 输入框下方 | 不遮挡用户的输入内容;目光流自然(输入框→下方错误信息) |
| 对比度 | 正文 ≥4.5:1 | WCAG AA,保证色弱用户能看清 |
第 1 部分:输入框类型选择(何时用哪个 type)
text(通用文本输入)
<input type="text" placeholder="输入用户名" />
什么时候用:
- 用户名、昵称
- 搜索框
- 任何不特定数据类型的输入
移动键盘:默认字母键盘
优点:
- 没有特殊格式要求
- 用户可以输入任何内容
缺点:
- 需要手动验证内容
email(邮箱输入)
<input type="email" placeholder="user@example.com" />
为什么用 type="email" 而不是 type="text":
-
移动键盘优化
- 浏览器自动弹出 @ 符号
- 省去用户长按找 @ 的麻烦
- 桌面浏览器的行为一样,不会有坏影响
-
原生浏览器验证
<input type="email" required />- 不需要写正则表达式
- 浏览器会在提交时自动检查格式
- 用户体验:提交时如果格式错,浏览器自动阻止 + 显示错误
-
语义化
- 代码的意图清晰(这是邮箱输入,不是普通文本)
- 屏幕阅读器会说“邮箱输入框”而不是“文本输入框”
什么时候不用:
- 不要用
type="text"然后自己写邮箱正则 - 不要以为
type="email"的验证够完美(不能检查邮箱是否真实存在)
对应案例场景:博客登陆表单中的邮箱字段
password(密码输入)
<input type="password" placeholder="••••••••" />
关键特点:
- 用户敲的内容显示为 ●●●●●(隐藏内容)
- 拷贝/粘贴功能仍然可用(用户可以从密码管理器粘贴)
为什么要隐藏密码:
- 安全性:防止背后看屏幕的人看到密码
- 但用户也需要确认“我输入的是什么”
解决方案:显示/隐藏切换
<div class="password-input-wrapper">
<input id="pwd" type="password" placeholder="••••••••" />
<button id="toggle-pwd" type="button" aria-label="显示密码">
<svg><!-- 眼睛 Icon --></svg>
</button>
</div>
<script>
const input = document.getElementById('pwd');
const btn = document.getElementById('toggle-pwd');
btn.addEventListener('click', () => {
const isPassword = input.type === 'password';
input.type = isPassword ? 'text' : 'password';
btn.setAttribute('aria-label', isPassword ? '隐藏密码' : '显示密码');
});
</script>
这样做的好处:
- 用户可以临时看到密码(验证输入正确)
- 仍然默认隐藏(安全默认)
- 按钮明确标注“显示密码”(用户主动选择)
对应案例场景:博客登陆表单中的密码字段
number(数字输入)
<input type="number" min="1" max="100" step="1" />
什么时候用:
- 年龄、数量、价格
- 任何明确是“数字”的输入
移动键盘:数字键盘(9 个数字 + 0 + 操作键)
原生特性:
- 浏览器内置上/下箭头(+1/-1)
- 自动检查 min/max 范围
- 非数字的输入会被拒绝
缺点:
- 某些场景(电话号码、邮编)用
number会失去前导 0 - 例如邮编 01234 会变成 1234(丢失信息)
何时不用 number:
<!-- ❌ 不要 -->
<input type="number" placeholder="13812345678" />
<!-- ✅ 要 -->
<input type="tel" placeholder="138-1234-5678" />
tel(电话号码)
<input type="tel" placeholder="138-1234-5678" />
为什么用 type="tel" 而不是 type="number":
- 保留所有字符(包括 - + 空格)
- 移动端弹电话键盘
- 不会自动去掉前导 0 或特殊符号
对应案例场景:如果博客有“我的联系方式”编辑,电话字段用 type="tel"
第 2 部分:输入框状态与视觉反馈
Normal 状态(未交互)
┌─────────────────────────┐
│ 邮箱地址 │ ← label
│ ┌───────────────────────┤
│ │ user@example.com │ ← 占位符或之前的值
│ └───────────────────────┤
│ 输入你的账户邮箱 │ ← helper text
└─────────────────────────┘
设计决策:
- 边框:Hairline 色 #E5E1D8(浅,暗示可交互但不突出)
- 背景:白色(#FFFFFF)
- 文字:Ink 色 #1C1B18
- 高度:44px(含 padding)
- 圆角:6px
Focus 状态(用户点击或 Tab 进入)
┌─────────────────────────┐
│ 邮箱地址 │
│ ┌───────────────────────┤ ← 边框变朱砂
│ │ user@ │ ← 光标在这里
│ └───────────────────────┤
│ 输入你的账户邮箱 │
└─────────────────────────┘
(圆形 focus ring) ← 外层 2px 朱砂边框
决策:
- 边框变色:Hairline → Cinnabar #C13E23
- 外层 outline:2px Cinnabar(focus ring)
- 背景保持白色(不需要变)
- 时间:200ms ease-out 过渡
为什么要 focus ring:
- 键盘用户(按 Tab)需要看到焦点在哪
- 屏幕阅读器用户也会听到“焦点进入邮箱字段”
- 对比度要足够(Cinnabar 5.28 on white,达 AA)
Filled 状态(用户已输入)
┌─────────────────────────┐
│ 邮箱地址 * │ ← required 星号
│ ┌───────────────────────┤
│ │ user@example.com │ ← 用户的输入
│ └───────────────────────┤
│ ✓ 邮箱格式正确 │ ← 可选:success 反馈
└─────────────────────────┘
决策:
- 如果验证通过,可以显示绿✓ Icon(可选)
- 如果字段是 required,用 * 标记
- 边框颜色可以保持 Hairline(已经表达出“有内容”的视觉了)
Error 状态(验证失败)
┌─────────────────────────┐
│ 邮箱地址 * │
│ ┌───────────────────────┤ ← 边框变错误色
│ │ xiaoming │ ❌ ← Icon + 错误提示
│ └───────────────────────┤
│ ✗ 邮箱格式不正确,请输入完整邮箱
└─────────────────────────┘
决策:
① 边框颜色
border-color: #A02C2C; /* Error 色(DESIGN.md)*/
② 错误位置:输入框下方
- 不要在输入框右侧挤 Icon(会遮挡内容)
- 错误信息完整可读很重要
③ 错误文案
- 清晰说明什么错了:「邮箱格式不正确」而不是「❌ 错误」
- 告诉用户怎么修复:「请输入完整邮箱(user@example.com)」
④ 时机
情景 1:实时验证(推荐)
用户输入 xiaoming → Blur(离开字段)
↓
系统检查格式
↓
显示错误「邮箱格式不正确」
↓
用户修改 → Blur 再检查 → 错误消失
情景 2:提交时验证
用户输入 xiaoming
用户点「登陆」
↓
系统检查所有字段
↓
显示所有错误
↓
用户修改后重新提交
哪个更好:
- 实时验证:用户体验更好(及时反馈)
- 提交时验证:用户可能会填一半的表单然后放弃
建议:邮箱字段用实时验证(字段少、可立即反馈)
Disabled 状态(用户无法交互)
┌─────────────────────────┐
│ 邮箱地址 │
│ ┌───────────────────────┤
│ │ user@example.com │ ← 文字变灰、光标不可用
│ └───────────────────────┤
│ 此字段已被管理员锁定 │ ← 解释为什么禁用
└─────────────────────────┘
决策:
- 背景:Paper 色(变浅,表达“不可用”)
- 文字:Ink-2 色(变灰)
- 光标:
cursor: not-allowed - Opacity:0.5(如果上述不够明显)
HTML:
<input type="email" disabled aria-disabled="true" />
什么时候禁用:
- 加载中(提交表单时输入框临时禁用)
- 权限不足(管理员限制某些用户编辑)
- 条件不满足(例如“有其他选择时,此字段不需要”)
第 3 部分:移动端优化
键盘处理
问题:移动端虚拟键盘会遮挡表单下半部分
解决方案:
<input type="email"
inputmode="email" <!-- 主动指定键盘类型 -->
autocomplete="email" <!-- 让浏览器自动填充 -->
spellcheck="false" <!-- 邮箱不需要拼写检查 -->
/>
inputmode 类型:
email:邮箱键盘(@)tel:电话键盘(0-9 + *)numeric:纯数字decimal:数字 + 小数点url:URL 键盘(/ : . )
autocomplete 值:
email:浏览器从已保存邮箱自动填充off:禁用自动填充(密码字段通常不用 off,让密码管理器工作)current-password:密码字段用这个(让 1Password、LastPass 识别)
字号不要低于 16px
问题:iOS Safari 会自动放大字号 ≤ 16px 的输入框
/* ❌ 不要 */
input {
font-size: 14px; /* iOS 会放大到 16px,然后页面跟着缩放 */
}
/* ✅ 要 */
input {
font-size: 16px; /* iOS 不会动放大 */
}
后果(如果字号 < 16px):
- 用户点输入框
- iOS 自动放大输入框
- 页面跟着放大
- 用户需要缩小页面才能看到全貌
- 体验糟糕
高度确保 ≥ 44px
iOS/Android 触达区标准:44x44px
/* ✅ 要 */
input {
height: 44px; /* 最小触达区 */
padding: 10px 12px; /* 内间距 */
}
好处:
- 不会误触邻近元素
- 用户不用放大页面也能点中
第 4 部分:可访问性
Label 关联
<!-- ❌ 不要 -->
<label>邮箱</label>
<input type="email" />
<!-- ✅ 要 -->
<label for="email">邮箱</label>
<input id="email" type="email" />
为什么:
- 屏幕阅读器可以说“邮箱输入框”而不是“输入框”
- 用户点 label 文字,焦点自动进入输入框(大幅提升移动端体验)
错误关联
<label for="email">邮箱 *</label>
<input
id="email"
type="email"
required
aria-invalid="false" <!-- 初始:无错 -->
aria-describedby="email-error"
/>
<p id="email-error" role="alert"></p>
<script>
input.addEventListener('blur', () => {
if (!input.validity.valid) {
input.setAttribute('aria-invalid', 'true');
errorMsg.textContent = '邮箱格式不正确';
} else {
input.setAttribute('aria-invalid', 'false');
errorMsg.textContent = '';
}
});
</script>
关键属性:
aria-invalid="true":告诉屏幕阅读器这个字段有错aria-describedby="email-error":错误信息在 id=“email-error” 的元素里role="alert":错误信息变化时立即读出(而不是用户要自己查询)
Focus Ring 可见
input:focus-visible {
outline: 2px solid #C13E23; /* Cinnabar */
outline-offset: 2px;
}
/* 不要移除 outline(会破坏键盘导航) */
input:focus {
outline: none; /* ❌ 禁止 */
}
为什么很重要:
- 键盘用户(包括屏幕阅读器用户)需要看到焦点
- 移除 outline 的网站对残障人士来说是“无法使用”的
常见错误
❌ 错误 1:只用 placeholder,没有 label
<!-- ❌ 不要 -->
<input type="email" placeholder="Enter your email" />
问题:
- placeholder 在用户开始输入时会消失
- 用户会忘记这个字段是干什么的
- 屏幕阅读器不知道这是什么字段
正确做法:
<!-- ✅ 要 -->
<label for="email">邮箱</label>
<input id="email" type="email" placeholder="user@example.com" />
❌ 错误 2:密码字段没有显示/隐藏切换
<!-- ❌ 不要 -->
<input type="password" placeholder="••••••••" />
问题:
- 用户无法验证自己输入的是什么
- 容易输错密码
正确做法:加显示/隐藏按钮(见上面 password 部分)
❌ 错误 3:邮箱输入用 type="text" + 正则表达式
<!-- ❌ 不要 -->
<input type="text" placeholder="邮箱地址" />
<script>
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; // 自己写正则
// ...验证逻辑
</script>
问题:
- 移动端没有 @ 快捷键
- 重复造轮子(浏览器已有原生验证)
- 代码复杂、容易出 bug
正确做法:
<!-- ✅ 要 -->
<input type="email" />
❌ 错误 4:输入框字号 < 16px
见上面“移动端优化”部分。
相关词条
- №03「Form 表单结构与校验」 — 多字段表单的整体设计
- №03「Button 怎么选」 — 输入框旁边通常有提交按钮
- №04「表单验证提示」 — 错误信息的设计
- №01「无障碍设计」 — 更多可访问性最佳实践
完整可复制的 Prompt
设计一个登陆表单,包含邮箱和密码两个输入框。
需求:
1. 邮箱字段
- type: email(移动端弹 @ 键盘)
- 实时验证(Blur 时检查格式)
- 错误时显示「邮箱格式不正确」
2. 密码字段
- type: password(内容隐藏)
- 显示/隐藏切换按钮(眼睛 Icon)
- 最小长度 8 字符
- Helper text:「至少 8 个字符」
3. 设计约束
- 配色:遵循 TellYourAgent 设计系统
* Normal border: Hairline #E5E1D8
* Focus border: Cinnabar #C13E23
* Error border: Error #A02C2C
- 高度:44px(移动端触达区)
- 字号:≥16px(防止 iOS 自动放大)
- 圆角:6px
- Focus ring:2px Cinnabar outline
4. 可访问性
- <label for="id"> 与 <input id="id"> 关联
- aria-invalid + aria-describedby(错误时)
- 键盘用户可以 Tab 遍历
- 屏幕阅读器可理解所有字段
5. 响应式
- 桌面:输入框自适应宽度,最大 400px
- 移动:full-width,padding 16px
输出:
- HTML(语义化标记)
- CSS(所有状态、焦点环、过渡动效)
- JavaScript(实时验证、显示/隐藏切换、错误管理)
- 使用说明
为什么这个决策很重要
输入框是用户“告诉”应用“我要什么”的唯一方式。
- 用
type="email"而不是type="text",移动用户少敲 5 次按键 - 44px 高而不是 32px,老年用户、手指大的用户就能按中
- 有 focus ring,盲人用键盘也能正常使用
- 实时验证而不是提交时验证,用户少走一次弯路
一句话:输入框的每个细节都在为“最广泛的用户群体”服务。