Figma 验证码输入框与一次性密码系统(OTP & PIN Code Field Architecture)组件架构实战:从等宽网格分布、光标聚焦态流转、一键粘贴自动分流到 Variables 条件状态机与 Dev Mode 前端 React OTP Input 代码交付
1. 业务痛点与一次性密码(OTP)输入框架构蓝图 #
在当今企业级 SaaS 身份认证、出海电商结账安全验证、银行与金融支付两步验证(Two-Factor Authentication, 2FA)、移动端短信快捷登录以及企业内网单点登录(SSO)风控场景中,一次性密码与数字验证码输入框(One-Time Password / PIN Code Input) 是用户高频接触的核心关键路径。
然而,在大量中后台与移动端设计资产中,很多设计团队常将验证码输入框视作几个零散的矩形方块与文本图层的简单编组(Group),缺乏系统化工程规范:
- 网格与间距缺乏弹性:在多端响应式断点适配时,固定尺寸的方块在小屏手机上直接发生左右溢出换行,导致输入界面破损;
- 状态机割裂脱节:仅绘制了全填满与全空白两种静态效果,完全忽略了光标当前聚焦(Focused Caret)、单格错误(Invalid Error State)、暗文圆点遮罩(Masked Dot)以及禁用冻结状态;
- 剪贴板交互断层:未定义用户复制短信或邮件 6 位数字验证码后,在第一格触发“粘贴(Paste)”时的整串自动分流回填逻辑;
- 前端交付标准模糊:直接将 6 个独立原生
<input>交付给研发,导致研发在处理移动端输入法弹起、退格键(Backspace)向左回跳、iOS 短信验证码自动填充(SMS Autofill)时出现各种卡顿与键盘闪烁异常。
一套工业级的 Figma OTP & PIN 输入框组件系统,必须将几何结构、组件属性(Component Properties)、动态变量(Variables)与前端可访问性(a11y)完全贯通:
┌────────────────────────────────────────────────────────┐
│ OTP / PIN Code Component System │
├────────────────────────────────────────────────────────┤
│ [ Digit 1 ] [ Digit 2 ] [ Digit 3 ] - [ Digit 4 ]...│
│ Active Filled Empty Empty │
│ (Caret Blinking) (Mask/Clear) │
├────────────────────────────────────────────────────────┤
│ Token Variables: sizing, border, color, focus-ring │
│ Interactive: Backspace return, Paste parsing │
│ Dev Mode: inputMode="numeric", autoComplete="one-..." │
└────────────────────────────────────────────────────────┘
结合站内 Figma Auto Layout 弹性自适应与流体布局深度实战 与 Figma Variables 变量与 Design Tokens 体系落地指南,本教程将系统拆解如何从 0 到 1 搭建高灵活性、高保真交互的 OTP 输入框组件库。
2. 原子单格(Slot Cell)几何拓扑与 Auto Layout 规范 #
实现稳定响应式 OTP 输入框的核心基础,在于将每个独立输入格封装为高度内聚的原子组件 otp-slot-cell。
2.1 单元格尺寸与间距阶梯 #
通常针对桌面端管理中台与移动端 APP,推荐设立两档标准化尺寸:
- Desktop / Tablet 舒适版:单格宽度
48px× 高度56px,圆角8px,水平内间距(Gap)12px; - Mobile 紧凑版:单格宽度
40px× 高度48px,圆角6px,水平内间距(Gap)8px。
otp-slot-cell (48px × 56px)
┌───────────────────────────────────────────────┐
│ │
│ 7 │ <- 居中文本 (Roboto/Inter Tabular)
│ │ │
│ │ <- Caret (2px × 24px) │
│ │
└───────────────────────────────────────────────┘
Border: 1.5px (Default: #D9D9D9, Focus: #1677FF)
2.2 内部 Auto Layout 布局参数设置 #
- 容器模式:创建 Frame,重命名为
otp-slot-cell; - 布局对齐:设置为 Auto Layout,对齐方式选择 Center (居中对齐);
- 尺寸约束:横向宽度设置为
Fixed (48px),纵向高度设置为Fixed (56px); - 内部图层:
- 数值文本层(Digit Text):字号设为
24px,字重设为Semi-Bold (600)。必须在 Typography 高级排版面板中启用 等宽数字(Tabular Figures / Monospace Numbers),防止不同数字(如 1 与 8)宽度不一致造成视觉抖动; - 光标条(Caret Bar):绘制矩形,宽度
2px,高度24px,圆角1px,填充主色变量token/color/brand-primary; - 掩码圆点(Mask Dot):绘制正圆
8px × 8px,用于密码或 PIN 码防偷窥遮蔽场景。
- 数值文本层(Digit Text):字号设为
3. 六大状态变体矩阵与组件属性(Component Properties) #
通过组合 Figma 的变体(Variants)与组件属性,我们可以消除冗余图层,构建清晰的状态机矩阵。
3.1 核心变体维度设计 #
| 变体属性 (Property) | 可选枚举值 (Options) | 布局表现与视觉规范 |
|---|---|---|
| State | Default, Hover, Focused, Filled, Error, Disabled | 驱动外边框粗细、色彩、背景底色及投影变化 |
| Masked | False (明文数字), True (圆点掩码) | 控制敏感密码场景下的明暗文隐藏 |
| Size | Medium (48x56), Small (40x48) | 适配桌面端表单弹窗与移动端移动视口 |
3.2 各状态关键视觉参数 #
- Default(空置默认态):
- 填充底色:
token/color/bg-container(纯白#FFFFFF); - 描边线框:
1px solid,颜色为token/color/border-default(#D9D9D9); - 光标与文字:均不可见。
- 填充底色:
- Hover(悬浮感知态):
- 描边线框高亮为品牌主色淡化层
token/color/brand-hover(#4096FF)。
- 描边线框高亮为品牌主色淡化层
- Focused(活动输入态):
- 描边线框升级为
1.5px solid,颜色绑定token/color/brand-primary(#1677FF); - 增加外发光环(Focus Ring):
0 0 0 3px rgba(22, 119, 255, 0.15); - 光标
Caret Bar显现,可在高保真演示中开启 Smart Animate 闪烁动效。
- 描边线框升级为
- Filled(已录入态):
- 文字层显示具体数字,或者在
Masked=True时显示黑色实体圆点; - 边框保持为默认灰线或微深灰色,光标自动后移。
- 文字层显示具体数字,或者在
- Error(校验失败态):
- 描边线框变红:
1.5px solid token/color/danger-border(#FF4D4F); - 外阴影警示:
0 0 0 3px rgba(255, 77, 79, 0.15); - 文本或圆点颜色变为危险警示红。
- 描边线框变红:
- Disabled(只读/倒计时冻结态):
- 背景变为浅灰:
token/color/bg-disabled(#F5F5F5); - 文本透明度降低为
38%,鼠标光标标注为not-allowed。
- 背景变为浅灰:
4. 4/6 位整组容器装配与分割符(Divider)排版 #
完成单格原子组件后,我们将其装配为具备业务完整性的 otp-input-group 容器组件。
4-Digit / 6-Digit Container Assembly
┌────────────────────────────────────────────────────────┐
│ [ Cell 1 ] [ Cell 2 ] [ Cell 3 ] - [ Cell 4 ] ... │
│ <---------- Auto Layout (Gap: 12px) --------------> │
└────────────────────────────────────────────────────────┘
4.1 容器 Auto Layout 配置 #
- 连续复制 6 个
otp-slot-cell实例; - 选中全部实例,按快捷键
Shift + A生成横向 Auto Layout 容器; - 主轴对齐:设置为横向
Horizontal,间距设置为12px; - 交叉轴对齐:设置为
Center (居中); - 水平缩放约束:容器设置为
Hug contents,若在移动端栅格内则将容器设为Fill container,并配合断点将单元格设为自适应或者微调 Gap。
4.2 3-3 分割破折号(Dash Divider)设计 #
在现代 6 位验证码排版中,为了降低用户的认知负荷与眼球扫视疲劳,国际主流规范通常在第 3 格与第 4 格之间插入一个中位分隔线(Hyphen / Dash):
- 创建一个宽度
12px、高度2px、圆角1px的灰色短杠图层; - 置入第 3 与第 4 格之间,绑定布尔属性
ShowDivider; - 当业务需要支持 4 位 PIN 码时,隐藏后 2 格并关闭
ShowDivider,Auto Layout 自动平滑聚拢闭合,无需手动重新计算间距。
更多系统化组件属性封装可参考 Figma 组件属性(Component Properties)企业级实战。
5. Figma Variables 交互原型:模拟真机输入、退格与整串粘贴 #
为了让产品经理、交互设计师与用户测试人员获得极致逼真的交互体验,我们可以利用 Figma 变量(Variables)与表达式逻辑构建完整的 OTP 原型状态机。
5.1 变量字典构建 #
在 Local Variables 面板中创建 Collection: OTP_Form:
| 变量名称 | 类型 (Type) | 初始值 | 作用描述 |
|---|---|---|---|
otp_cursor_index | Number | 1 | 标记当前正在激活的方格序号(1~6) |
otp_val_1 ~ otp_val_6 | String | "" | 记录 1 到 6 位的具体字符数值 |
otp_is_error | Boolean | false | 标记是否触发验证码错误红框提示 |
otp_is_masked | Boolean | false | 控制明文与黑点掩码切换 |
5.2 状态联动与条件动作流(Conditional Logic) #
- 单格点击聚焦(Click to Focus):
- 点击第 N 个方格,触发动作:
Set variable otp_cursor_index = N; - 对应方格的变体状态根据
otp_cursor_index == N条件驱动显示为Focused。
- 点击第 N 个方格,触发动作:
- 模拟逐字输入(Key Press Simulation):
- 监听按键输入
0~9:- 执行:
Set variable otp_val_[index] = "KeyChar"; - 继而执行:
Set variable otp_cursor_index = otp_cursor_index + 1; - 焦点自动顺延跳至下一格,上一格变为
Filled状态。
- 执行:
- 监听按键输入
- 退格键回退删除(Backspace Navigation):
- 监听退格按键
Backspace:- 若当前格为空,则执行:
Set variable otp_cursor_index = otp_cursor_index - 1,将上一格字符置空并将焦点回拨; - 若当前格已有数字,则清空当前格并保留在当前格。
- 若当前格为空,则执行:
- 监听退格按键
- 一键整串粘贴(Paste Shortcut):
- 在第一格添加交互热区,点击或按下快捷键时:一次性批量赋值
otp_val_1到otp_val_6,并将otp_cursor_index直接置为6,模拟从剪贴板自动解析验证码的流畅体验。
- 在第一格添加交互热区,点击或按下快捷键时:一次性批量赋值
深入的条件逻辑配置方法可查阅 Figma 高级原型与条件逻辑实战。
6. Dev Mode 与现代前端 React / input-otp 规范代码交付 #
许多前端开发在实现 OTP 输入框时,经常采用 6 个独立 <input> 元素。然而,这种方案在移动端输入法(IME)切换、光标位置计算与无障碍阅读器中存在严重缺陷。现代前端工程界推荐的最佳架构是:单隐形输入框驱动(Underlying Hidden Input)+ 视觉等宽槽位镜像(Slot Mirroring)。
依托 Figma Dev Mode 与前端代码交付规范 以及 Figma Code Connect 企业级落地指南,可以直接将 Figma 变体参数与主流第三方库(如 input-otp,即 shadcn/ui 推荐底层)进行 1:1 无损映射。
// VerificationOtpInput.tsx
import React from 'react';
import { OTPInput, SlotProps } from 'input-otp';
interface OtpProps {
maxLength?: number;
isMasked?: boolean;
hasError?: boolean;
value: string;
onChange: (value: string) => void;
onComplete?: (code: string) => void;
}
export const VerificationOtpInput: React.FC<OtpProps> = ({
maxLength = 6,
isMasked = false,
hasError = false,
value,
onChange,
onComplete,
}) => {
return (
<OTPInput
maxLength={maxLength}
value={value}
onChange={onChange}
onComplete={onComplete}
// 移动端核心属性:直接呼出数字专用键盘,允许读取短信验证码
inputMode="numeric"
autoComplete="one-time-code"
pattern="[0-9]*"
aria-label="请输入 6 位短信验证码"
containerClassName="flex items-center gap-3 justify-center select-none"
render={({ slots }) => (
<div className="flex items-center gap-2">
{slots.slice(0, 3).map((slot, idx) => (
<SlotCell key={idx} {...slot} isMasked={isMasked} hasError={hasError} />
))}
{/* 3-3 中部分隔横杠 */}
<div className="w-3 h-0.5 bg-gray-300 rounded mx-1" aria-hidden="true" />
{slots.slice(3, 6).map((slot, idx) => (
<SlotCell key={idx + 3} {...slot} isMasked={isMasked} hasError={hasError} />
))}
</div>
)}
/>
);
};
// 单格镜像渲染组件:严格还原 Figma 变体规范
const SlotCell: React.FC<SlotProps & { isMasked?: boolean; hasError?: boolean }> = ({
char,
isActive,
hasFakeCaret,
isMasked,
hasError,
}) => {
return (
<div
className={`
relative w-12 h-14 flex items-center justify-center rounded-lg text-2xl font-semibold
transition-all duration-150 tabular-nums border
${hasError
? 'border-red-500 ring-2 ring-red-100 bg-red-50 text-red-600'
: isActive
? 'border-blue-600 ring-4 ring-blue-100 bg-white text-gray-900 shadow-sm'
: 'border-gray-300 bg-white text-gray-900 hover:border-blue-400'
}
`}
>
{/* 字符显示:支持圆点掩码与明文切换 */}
{char ? (
isMasked ? (
<span className="w-2.5 h-2.5 rounded-full bg-gray-900 inline-block" />
) : (
<span>{char}</span>
)
) : null}
{/* 活跃光标模拟 */}
{hasFakeCaret && (
<div className="absolute w-0.5 h-6 bg-blue-600 animate-pulse rounded-full pointer-events-none" />
)}
</div>
);
};
5.3 移动端与无障碍(a11y)合规关键点 #
autoComplete="one-time-code":在 iOS Safari 与 Android Chrome 中自动触发键盘顶部的“来自短信的验证码”一键点填;inputMode="numeric":确保任何智能手机在唤起虚拟键盘时,默认弹出大按键的九宫格纯数字键盘,避免弹出全键盘增加误触率;- 屏幕阅读器支持:外层容器提供合法的
aria-label与aria-invalid状态,确保视障群体在读屏软件下清晰了解当前聚焦的验证码位次。详细指南可参考 Figma 无障碍设计(Accessibility & a11y)合规落地实战。
7. 架构方案比对与选型总结 #
下表总结了在 Figma 设计系统建设与工程落地中,主流 OTP 输入框方案的权衡利弊:
| 维度 / 方案 | 传统分立 6-Inputs 方案 | 单隐形原生 Input + 视觉 Slot 镜像方案 (推荐) | 纯静态 Frame 编组方案 |
|---|---|---|---|
| 设计系统维护成本 | 高(需维护 6 个实例的分立属性) | 低(原子 Slot 配合容器统一分发属性) | 极低(但无法复用与响应扩展) |
| 退格与跨格焦点跳转 | 逻辑极易出错,光标容易失焦丢失 | 极其平滑,由统一指针天然维护索引 | 无逻辑支持 |
| 短信剪贴板粘贴回填 | 极易截断,需写大量原生事件监听 | 天然原生支持,直接匹配粘贴文本 | 不支持 |
| 移动端虚拟键盘兼容 | 易引起键盘连续收起弹起抖动 | 键盘完全静止无闪烁,性能优异 | 无从考量 |
| Figma 与代码同构度 | 中等 | 高度同构,与 Dev Mode 属性 100% 对应 | 脱节严重 |
通过规范化的原子 Slot 单元格抽取、Auto Layout 等宽网格、严谨的 6 态变体以及现代隐形 Input 代码模式,设计团队不仅能为业务产品提供像素级精细的高保真验证界面,更能赋能前端研发团队在数十分钟内高质量完成健壮的生产级代码部署。进一步探索设计令牌工程化,请参考 设计系统 Design Tokens 规范速查。
核心手册与速查工具
高频手册与实用设计生产力工具直达常见问题与答疑 (FAQ)
高频疑难解析与避坑指南完全不需要。绝大多数核心功能在免费 Starter 计划中均可完整体验与实操练习,无需额外付费购买付费团队版。