Figma 开关、复选框与单选框系统(Switch, Checkbox & Radio System)原子组件架构实战:从 14 态流转矩阵、48px 无障碍热区扩充(Hit Slop)、半选态(Indeterminate)到 Variables 布尔状态机与 Dev Mode 前端无障碍代码交付

2026 年 11 月 13 日

在任何企业级 Web 平台、移动端 App 或 SaaS 设计系统中,开关(Switch)、复选框(Checkbox)与单选框(Radio) 都是使用频次最高、交互细节最严苛的原子级表单控件(Atomic Form Controls)。它们虽在视觉上仅占数十像素,却直接承载了用户的决策提交与状态切换。

然而,在日常设计系统维护与跨职能团队协作中,基础选择控件往往潜藏着大量工程与体验缺陷:

  1. 交互状态残缺:大部分设计师仅绘制了选中(Checked)与未选(Unchecked)两种常态,遗漏了 Hover、Pressed、Focus-Visible(无障碍键盘焦点轮廓)以及只读和多维禁用态;
  2. 触控与点击热区过窄(Hit Area Trap):直接以 16px 或 20px 的视觉图形作为组件物理边界,在移动端与触控屏上极易造成点击失效或误触相邻元素,违背 WCAG 2.2 AA 级无障碍规范;
  3. 文本换行基线错位:当选项标签包含多行长文本时,若未在 Auto Layout 容器布局指南 中正确配置纵向对齐策略,图元会尴尬地悬浮在多行文本的绝对几何中心,破坏版面阅读流;
  4. 半选状态(Indeterminate)缺失:在处理树形组织架构、权限管理或多选表格时,无法准确表达“部分子项被勾选”的中介态;
  5. 研发交付语义断层:前端开发在 Dev Mode 开发模式 中提取到的是无语义的矩形与圆形矢量打组,难以直接无损映射至原生的 <input type="checkbox">、<input type="radio"> 或现代无障碍组件库。

本文将深入拆解 Switch、Checkbox 与 Radio 的原子物理结构,构建包含 14 种交互态的完整组件集,实战 48px 热区扩充与 Variables 级联逻辑,并输出符合 WAI-ARIA 规范的前端工程代码。


三大表单选择控件的交互心理学与选型矩阵 #

许多产品在设计表单时经常混淆 Switch 与 Checkbox 的使用边界。明确三者的核心交互模型是构建标准化组件库的前提:

控件类型 核心交互模型 生效时机 适用场景示例 对应 WAI-ARIA 角色
Switch 开关 物理拨动开关(二值状态) 立即生效(无需表单额外点击保存) 系统深色模式切换、消息推送开启、硬件功能启闭 role="switch" (aria-checked="true\|false")
Checkbox 复选框 独立多项复选(支持未选/选中/半选) 延迟生效(随同父表单 Submit 提交) 购物车商品多选、权限列表分配、表格批量操作 role="checkbox" (aria-checked="mixed\|true\|false")
Radio 单选框 互斥单项单选(同一分组内有且仅有一项) 延迟或即时(同一 Group 内互斥) 支付方式选择(微信/支付宝/信用卡)、套餐版本选配 role="radio" (aria-checked="true\|false")
┌────────────────────────────────────────────────────────────────────────┐
│                        Form Selection Architecture                     │
├──────────────────────────┬──────────────────────────┬──────────────────┤
│       1. Switch          │       2. Checkbox        │     3. Radio     │
│    (Instant Trigger)     │    (Multi-Select Form)   │ (Mutually Exclusive)│
│                          │                          │                  │
│   ┌───────────────┐      │     ┌──────┐             │     ╭──────╮     │
│   │   [●] OFF     │      │     │  ✔   │ Checked     │    (   ●  ) Active│
│   │   OFF [●]     │      │     ├──────┤             │     ╰──────╯     │
│   └───────────────┘      │     │  ─   │ Indeterminate│     ╭──────╮     │
│                          │     ├──────┤             │    (      ) Normal│
│                          │     │      │ Unchecked   │     ╰──────╯     │
│                          │     └──────┘             │                  │
└──────────────────────────┴──────────────────────────┴──────────────────┘

14 态流转状态矩阵与变体属性(Component Properties)解耦 #

为确保组件在各种输入设备(鼠标、触控板、键盘、触屏)下均具备严密的可用性,必须在 组件与变体深度指南 中为每个控件建立全状态流转矩阵。

1. 状态维度拆解(State Matrix) #

每个控件均由两组正交维度复合而成:

  1. Value 状态:
    • Checkbox: Unchecked、Checked、Indeterminate(半选横杠);
    • Switch: Off、On;
    • Radio: Unchecked、Checked。
  2. Interaction 交互态:
    • Default(静止常态);
    • Hover(鼠标悬停,底色或描边轻度微调);
    • Pressed / Active(鼠标按下或触摸按压瞬间,带有涟漪或微缩放);
    • Focus-Visible(键盘 Tab 导航选中的高亮无障碍双层焦点环,Outline 2px Offset);
    • Disabled(禁用灰度态,Cursor: not-allowed,文本透明度 40%)。

两组维度交叉组合,构成了严密的 14 态完整架构。

2. 变体属性配置建议: #

在 Figma 主组件中,推荐采用以下清晰命名:

  • Checked: false / true / mixed(布尔或枚举,推荐用于代码属性对应);
  • State: Default / Hover / Pressed / Focused / Disabled;
  • Size: Medium(默认 20px 图元 / 40px 行高) / Small(16px 图元 / 32px 行高);
  • ShowLabel: true / false(文本插槽开关);
  • Description: true / false(次级辅助说明文本开关)。

48px 无障碍触控热区扩充(Hit Slop)与基线对齐实操 #

在移动设备与小屏幕设备上,苹果 iOS HIG 与谷歌 Material Design 均明确要求可交互元素的最小触控热区不得低于 44×44pt / 48×48dp。若直接将 20px 的复选框交付给工程师,将产生严重的操作困难。

1. 结构化热区透明包裹层(Hit Slop Padding) #

不要直接在裸露的矢量图元上绑定点击交互!标准结构应为:

┌────────────────────────────────────────────────────────┐
│ Selection Control Item (Auto Layout Horizontal: Gap 8px)│
│                                                        │
│  ┌──────────────────────────────────────────────────┐  │
│  │ Hit Target Box (Frame: 44×44px or 48×48px)       │  │
│  │ Auto Layout: Align Center / Center               │  │
│  │ Fill: None (Transparent)                         │  │
│  │ Clip Content: OFF                                │  │
│  │                                                  │  │
│  │   ┌──────────────────────────────────────────┐   │  │
│  │   │ Visual Indicator (Frame: 20×20px)        │   │  │
│  │   │ Border / Background / Check Icon / Thumb │   │  │
│  │   └──────────────────────────────────────────┘   │  │
│  └──────────────────────────────────────────────────┘  │
│                                                        │
│  ┌──────────────────────────────────────────────────┐  │
│  │ Text Content Container (Auto Layout Vertical)    │  │
│  │ Width: Fill container                            │  │
│  │ ├─ Label Text (Body Regular 14px, Primary Color) │  │
│  │ └─ Helper Text (Caption 12px, Secondary Color)   │  │
│  └──────────────────────────────────────────────────┘  │
└────────────────────────────────────────────────────────┘

2. 多行文本折行时的基线对齐避坑实操 #

当 Label 文本较长并自动换行时,常见的严重设计错误是将外层 Auto Layout 设置为居中对齐(Align Center),导致勾选框垂直居中在整段多行文本中间。

正确工程对齐方案:

  1. 外层容器的 Auto Layout 对齐模式强制设置为 Align Top Left(顶部居左对齐);
  2. 检查首行文本的行高(Line Height)。例如 14px 字体配合 20px 行高时,首行文本的视觉中心距离顶部为 10px;
  3. 将视觉图元(20px 高度)的顶部内边距或容器上边缘进行微调偏移,保证图元的垂直几何中线与首行文本的基线视觉中线 1:1 像素级水平重合。

更多对齐与响应式容器技巧可参考 Auto Layout 容器布局指南。


Figma Variables 变量绑定与父子半选(Indeterminate)级联状态机 #

利用 Figma 的数值与布尔型变量系统,无需第三方插件即可制作高保真的全选、反选与半选级联原型。

1. 变量定义清单 #

在 Local Variables 面板中建立以下变量集合:

  • selectAll: Boolean(控制全选复选框的状态,默认 false);
  • isIndeterminate: Boolean(控制全选复选框是否显示半选横杠,默认 false);
  • itemChecked1: Boolean(子选项 1 选中态,默认 false);
  • itemChecked2: Boolean(子选项 2 选中态,默认 false);
  • selectedCount: Number(当前选中的子项总数,默认 0)。

2. 交互逻辑步骤化配置: #

  1. 子项点击触发器(Item Click Trigger):
    • 子项 1 绑定 OnClick -> Set variable:itemChecked1 = !itemChecked1;
    • 随之添加条件判断(Conditional):
      • If (itemChecked1 == true and itemChecked2 == true):selectAll = true, isIndeterminate = false;
      • Else if (itemChecked1 == false and itemChecked2 == false):selectAll = false, isIndeterminate = false;
      • Else:selectAll = false, isIndeterminate = true。
  2. 父级全选点击触发器(Master Checkbox Trigger):
    • 父级点击时:若当前 selectAll == true,则将 selectAll = false, itemChecked1 = false, itemChecked2 = false, isIndeterminate = false;
    • 若当前 selectAll == false,则一键将 selectAll = true, itemChecked1 = true, itemChecked2 = true, isIndeterminate = false。
  3. 图层可见性与变体模式联动:
    • 将主组件中的“选中”图层绑定至 selectAll,将“半选横杠”图层绑定至 isIndeterminate。

通过上述逻辑,设计团队可以在产品走查或客户汇报中,实时展示完全拟真的企业级多选级联交互。更详尽的变量体系规范参见 Design Tokens 标准命名与结构对照手册。


Dev Mode 前端工程化交付与 WAI-ARIA 无障碍标准 #

在 Dev Mode 开发模式 中交付选择控件时,前端工程师最忌讳设计师使用自定义 <div> 模拟表单。以下是基于标准语义化 HTML5 与现代 Tailwind CSS / React 的无障碍代码交付标准:

1. Checkbox 复选框现代 React 组件规范 #

import React, { useEffect, useRef } from 'react';

interface CheckboxProps {
  id: string;
  label: string;
  description?: string;
  checked: boolean;
  indeterminate?: boolean;
  disabled?: boolean;
  onChange: (checked: boolean) => void;
}

export const EnterpriseCheckbox: React.FC<CheckboxProps> = ({
  id,
  label,
  description,
  checked,
  indeterminate = false,
  disabled = false,
  onChange,
}) => {
  const inputRef = useRef<HTMLInputElement>(null);

  // 原生 input 的 indeterminate 属性只能通过 JS DOM 属性赋值
  useEffect(() => {
    if (inputRef.current) {
      inputRef.current.indeterminate = indeterminate;
    }
  }, [indeterminate]);

  return (
    <label
      htmlFor={id}
      className={`group relative flex items-start gap-3 cursor-pointer select-none py-1.5 ${
        disabled ? 'cursor-not-allowed opacity-50' : ''
      }`}
    >
      {/* 44px 移动端无障碍可点击热区扩充容器 */}
      <div className="flex h-5 w-5 items-center justify-center shrink-0 mt-0.5">
        <input
          ref={inputRef}
          id={id}
          type="checkbox"
          checked={checked}
          disabled={disabled}
          onChange={(e) => onChange(e.target.checked)}
          className="peer sr-only" /* 隐藏原生外观但保留键盘可聚焦焦点链 */
          aria-checked={indeterminate ? 'mixed' : checked}
        />
        {/* 自定义视觉渲染容器与 Focus 焦点轮廓 */}
        <div
          className="h-4 w-4 rounded border border-gray-300 bg-white transition-all 
            peer-focus-visible:ring-2 peer-focus-visible:ring-indigo-600 peer-focus-visible:ring-offset-2
            peer-checked:bg-indigo-600 peer-checked:border-indigo-600
            peer-indeterminate:bg-indigo-600 peer-indeterminate:border-indigo-600
            group-hover:border-indigo-500"
        >
          {/* 选中对勾 SVG */}
          {checked && !indeterminate && (
            <svg className="h-full w-full stroke-white p-0.5" viewBox="0 0 16 16" fill="none">
              <path d="M3.5 8.5L6.5 11.5L12.5 4.5" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
          )}
          {/* 半选横杠 SVG */}
          {indeterminate && (
            <svg className="h-full w-full stroke-white p-0.5" viewBox="0 0 16 16" fill="none">
              <path d="M4 8H12" strokeWidth="2" strokeLinecap="round" />
            </svg>
          )}
        </div>
      </div>

      {/* 文本内容排版容器 */}
      <div className="flex flex-col text-sm leading-5">
        <span className="font-medium text-gray-900">{label}</span>
        {description && <span className="text-gray-500 text-xs mt-0.5">{description}</span>}
      </div>
    </label>
  );
};

2. Switch 开关无障碍 CSS 与 ARIA 属性交付 #

<!-- Switch 交付必须明确包含 role="switch" 与 aria-checked -->
<button
  type="button"
  role="switch"
  aria-checked="true"
  aria-label="启用双重身份验证"
  class="relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent 
         bg-indigo-600 transition-colors duration-200 ease-in-out 
         focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
>
  <span
    aria-hidden="true"
    class="pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow-lg ring-0 
           transition duration-200 ease-in-out translate-x-5"
  ></span>
</button>

常见失误与工程化自检清单 #

在最终发布组件库至团队全局库前,建议对照以下关键项进行验收:

  1. [ ] 热区是否达标:是否已在底层封装了不小于 44×44px 的透明热区;
  2. [ ] 焦点态(Focus Ring)是否独立存在:是否单独配置了符合 WCAG 2.2 双层高对比度焦点环,杜绝单纯依靠变色来表达键盘聚焦;
  3. [ ] 图层命名是否 1:1 严格对称:变体切换时图标和文本命名必须完全一致,防止实例属性重置;若遇变体切换重置问题,可查阅 Figma 常见报错与疑难排查手册;
  4. [ ] 暗色模式与语义 Token 映射:控件边框色与背景色是否全部挂载至语义颜色变量(如 border-control-default、bg-control-checked),详见 Design Tokens 标准命名与结构对照手册。

❓ 常见问题与答疑 (FAQ)

高频疑难解析与避坑指南
A

完全不需要。绝大多数核心功能在免费 Starter 计划中均可完整体验与实操练习,无需额外付费购买付费团队版。