验证码输入 OTP
输入短信 / 邮件验证码、激活码。每个字符一格显示,但实际是一个透明的原生输入框覆盖在格子上:粘贴整段、iOS / Android 自动填充验证码、读屏、表单提交都和普通输入框一样工作。
examples/otp/basic.html
试一试
- 直接粘贴
123 456或123-456,非数字字符会被去掉 - 按退格逐位删除;光标始终在末尾
状态与变体
examples/otp/variants.html
- 错误:给输入框加
aria-invalid="true",所有格子描边变红 - 禁用:给输入框加
disabled - 字母数字:根元素加
data-type="alphanumeric",自动转大写;可以在格子之间插入分隔符(aria-hidden的<span>) - 长度:由输入框的
maxlength决定,格子数量与之一致;紧凑尺寸把格子size-12 text-xl换成size-10 text-lg
完整的验证流程
examples/otp/verify.html
输满 6 位自动提交(data-autosubmit),验证失败后清空并重新聚焦;「重新发送」使用倒计时按钮。
结构
html
<label for="code" class="text-sm font-medium">邮箱验证码</label>
<div class="group relative flex w-fit gap-2" data-cb-otp>
<input id="code" type="text" data-cb-otp-input name="code" maxlength="6"
inputmode="numeric" autocomplete="one-time-code" required
class="absolute inset-0 z-10 h-full w-full bg-transparent text-transparent caret-transparent outline-none …" />
<div data-cb-otp-slot aria-hidden="true"
class="flex size-12 items-center justify-center rounded-lg border border-input font-mono text-xl
data-active:border-brand data-active:ring-3 data-active:ring-ring/20
group-has-[input[aria-invalid=true]]:border-destructive"></div>
<!-- 共 maxlength 个 slot -->
</div>| 属性 / 方法 | 说明 |
|---|---|
maxlength(写在 input 上) | 验证码长度 |
data-type="alphanumeric" | 允许字母,默认只允许数字 |
data-autosubmit | 输满后调用所在表单的 requestSubmit() |
cb:otp-change | 每次变化,detail = { value } |
cb:otp-complete | 输满时,detail = { value } |
CbtisUI.setOtp(el, '') | 设置或清空 |
格子的状态由脚本写入的属性表达:当前格 data-active,已填写 data-filled,当前空格内会插入一个闪烁的假光标。
使用建议
推荐
- 必须保留
autocomplete="one-time-code":手机键盘会直接提示短信里的验证码 - 输入框上方写清楚验证码发到了哪里(「已发送至 name@company.com」)
- 输满自动提交时,提交按钮仍然保留,给自动提交失败或关闭了脚本的情况兜底
- 验证失败后清空并聚焦,错误文案告诉用户下一步(重新输入 / 重新获取)
避免
- 用 6 个独立的
<input>实现:粘贴、自动填充、读屏体验都会变差 - 禁止粘贴
无障碍
- 读屏只会读到一个输入框(标签 + 提示 + 已输入的内容),格子全部
aria-hidden - 标签可以视觉隐藏(
sr-only),但不能省略 - 输入框字号为 16px,iOS 聚焦时不会自动放大页面
与 v2 的差异
| v2 现状 | 规范 |
|---|---|
EmailVerifyDialog、ResumeApplicationDialog 使用普通输入框,前端只做 6 位数字校验 | 验证码输入组件,输满自动提交 |