Design Systems 和 Component-Driven Development 如何缓解 UI 开发摩擦?
现代 Design Systems 和 Component-Driven Development 能减少 UI 开发中的重复工作。它们把常用的视觉规则、交互行为和实现细节集中到共享基础设施中,让业务开发者更多地通过组件契约、状态模型和测试来构建界面。
工程师因此不必各自处理所有设计判断,但仍需根据使用场景决定如何组织用户任务、先呈现哪些信息,以及操作失败后如何恢复。把“实现 UI”笼统归结为艺术性思维,已不足以指导工程实践;组件库能承担的用户体验工作也有边界。
先区分技术标准与工程方法
这些方案分属不同层次,不能统称为“行业标准”。
| 层次 | 可依据的规范或方法 | 主要作用 |
|---|---|---|
| 无障碍要求 | WCAG | 提供可验证的可访问性要求 |
| 控件交互 | WAI-ARIA、ARIA Authoring Practices Guide(APG) | 定义语义,并指导键盘操作、焦点管理等行为 |
| 设计数据交换 | Design Tokens Community Group(DTCG)规范 | 统一 token 的表达与交换格式 |
| 开发组织方式 | Component-Driven Development、设计系统治理 | 组织组件实现、文档、复用和验证 |
WCAG 和 WAI-ARIA 是 W3C 技术标准,APG 是指导性资料。APG 官方说明明确区分了两者。DTCG 规范属于社区组规范,不在 W3C 标准轨道内,不能称为 W3C Recommendation,详见 DTCG FAQ。
项目可以据此制定自己的工程约束,无需寻找一套覆盖所有 UI 工作的统一标准。
认知负担如何重新分配
设计 token 减少局部选择
如果每个页面都由开发者自行决定字号、间距、颜色和圆角,即使单次选择合理,组合起来也容易失去一致性。
设计系统可以分层管理这些决定:
- 基础 token 保存颜色、尺寸等原始值。
- 语义 token 表达用途,例如
color.text.secondary、color.action.primary。 - 组件内部将语义 token 映射到具体样式。
业务开发者选择“次要文字”时,就不必重新判断该用哪一种灰色。切换主题时,也可以集中调整映射,减少逐页修改。
Style Dictionary支持将平台无关的 token 数据转换为不同平台需要的形式。工具负责转换,团队仍需确定 token 的命名、用途和适用范围。
重复决策集中维护后,可以通过接口复用。设计系统维护者承担了更多前期工作,页面开发者需要记住的细节则随之减少。
组件契约封装重复的交互知识
一个可复用的对话框需要处理关闭操作、焦点进入与返回、键盘导航、可访问名称等问题。基础组件封装这些行为后,使用者就能集中编写业务逻辑:何时打开、显示什么,以及确认后发生什么。
不同工具提供的能力也有区别:
| 工具 | 主要提供什么 | 仍需开发者处理什么 |
|---|---|---|
| Tailwind CSS | 用 utility classes 表达样式,并支持预设尺度 | 布局选择、语义和交互行为 |
| Radix Primitives | 通用交互原语,处理许多键盘、焦点与 ARIA 细节 | 产品样式、业务流程和正确组合 |
| shadcn/ui | 可纳入项目并修改的组件源码及默认样式 | 本地修改、升级和组件一致性的维护 |
| Web Components | 自定义元素等浏览器组件机制 | 组件自身的设计、行为和无障碍实现 |
具体可参阅 Tailwind 文档、Radix 无障碍说明、shadcn/ui 文档和 HTML Custom Elements 标准。
Component-Driven Development 是一种开发方式,需要团队把组件作为独立单元开发,定义其输入、事件和可观察状态,再将组件组合成页面。采用上述工具,并不会自动建立这套工作方式。
显式状态让 UI 更容易推理
后端工程师熟悉的状态建模同样适用于界面。例如,同时使用 isLoading、isSuccess、hasError,就需要额外约束它们的组合;使用互斥的 idle | saving | saved | error,则可以直接表达哪些状态合法。
React 的状态结构指南也建议避免矛盾、冗余和重复状态。这种建模不依赖 React。对于复杂流程,还可以使用有限状态机或状态图,明确事件、转换条件和副作用。
在 Storybook 中,每个重要状态都可以有独立查看的实例,方便开发者查看超时、权限不足或空数据时的界面,省去反复启动完整业务流程的步骤。Storybook 将 story 定义为组件某个渲染状态的描述。
一套可执行的工作流程
1. 先定义用户任务和状态,再拆组件
以“修改收货地址”为例,先确认:
- 用户需要哪些信息才能完成修改?
- 校验失败时保留哪些输入?
- 保存超时后,用户如何判断是否成功?
- 用户没有权限时,页面提供什么说明?
再据此确定页面模式和组件边界。直接按照数据库表或接口字段生成表单,容易把存储结构带进用户操作流程。
选择设计系统作为参考时,也要看它是否提供任务模式,以及如何使用按钮等组件。GOV.UK Design System 的 Patterns围绕具体用户任务和页面类型提供指导,说明如何组合组件,并根据使用场景调整。
2. 建立稳定的实现边界
可以采用这样的分层:
Token 与主题
↓
基础交互组件
↓
业务模式组件
↓
页面与用户流程
API 数据 → 适配层/状态模型 → 页面与业务组件
例如,适配层把接口数据和错误转换为页面需要的状态,地址编辑组件处理字段、校验提示和保存反馈。标签关联、焦点样式等通用能力则由基础输入框负责。
这样的分工便于独立验证组件,也能避免接口字段的变化直接影响所有 UI 细节。
3. 把约定变成工具链约束
项目可以逐步引入以下规则:
- 用 TypeScript 限定组件变体和互斥状态。
- 通过 ESLint 导入规则,让业务代码优先使用公共 UI 包。
- 用 Stylelint 或自定义检查限制公共组件中的任意颜色、间距值。
- 要求公共组件变更附带相应的 story、评审和迁移说明。
制定约束时,也要明确如何扩展。出现新需求后,开发者应能提出新变体或新模式。缺少这条路径,开发者可能会不断绕过公共组件,设计系统也就增加了开发阻力。
4. 用多种验证覆盖不同问题
| 验证方式 | 适合发现的问题 |
|---|---|
| 类型检查 | 非法属性组合、数据结构不匹配 |
| 组件交互测试 | 点击、输入、键盘操作和状态变化错误 |
| 视觉回归测试 | 间距、换行、主题等外观变化 |
| 自动无障碍检查与人工检查 | 部分语义问题,以及键盘、焦点和辅助技术使用问题 |
| 完整流程验证与用户观察 | 组件组合后的流程问题、任务理解困难 |
Storybook 的测试体系支持围绕组件状态组织多种检查。评估测试结果时,需要明确每项检查能说明什么:截图一致只能说明外观没有出现预期之外的变化,无法证明任务流程合理。
代码示例:把状态和视觉规则收进组件
这个 React 和 TypeScript 保存操作组件由调用方控制。调用方处理请求,组件接收状态和事件。
type SaveState =
| { status: "idle" }
| { status: "saving" }
| { status: "saved" }
| { status: "error"; message: string };
type SaveActionProps = {
state: SaveState;
onSave: () => void;
};
export function SaveAction({
state,
onSave,
}: SaveActionProps) {
const saving = state.status === "saving";
const label = saving
? "保存中…"
: state.status === "error"
? "重试保存"
: "保存";
return (
<div>
<button
type="button"
className="save-action"
disabled={saving}
onClick={onSave}
>
{label}
</button>
<p role="status">
{saving && "正在保存"}
{state.status === "saved" && "已保存"}
</p>
<p role="alert">
{state.status === "error" && state.message}
</p>
</div>
);
}
样式通过语义变量引用设计规则。以下数值仅用于示例,实际项目应使用经过确认的 token 和主题:
:root {
--color-action-primary: #1d4ed8;
--color-text-on-action: #ffffff;
--color-focus-ring: #111827;
--space-control-block: 0.625rem;
--space-control-inline: 1rem;
--radius-control: 0.375rem;
}
.save-action {
font: inherit;
color: var(--color-text-on-action);
background: var(--color-action-primary);
padding:
var(--space-control-block)
var(--space-control-inline);
border: 0;
border-radius: var(--radius-control);
}
.save-action:focus-visible {
outline: 2px solid var(--color-focus-ring);
outline-offset: 3px;
}
.save-action:disabled {
cursor: wait;
}
组件中的约束包括:
message只存在于错误状态,减少无效数据组合。- 调用者通过状态控制反馈,无需分别操作按钮和提示文字。
- 视觉值集中在主题变量中,业务调用点无需重复选择颜色和间距。
- 原生
<button>保留浏览器提供的按钮语义与键盘行为。
调用方仍需正确更新请求状态,并将服务端错误转换为用户能理解的提示。禁用按钮也不能代替服务端对重复请求的处理。这个示例只覆盖组件边界,完整的保存流程还需要处理这些问题。
Figma-to-Code 应如何接入
从设计到代码,可以围绕已有组件建立映射:
- 对齐 Figma 组件与代码组件的名称、属性和变体。
- 明确 token 的权威来源、同步方向和冲突处理方式。
- 让生成或辅助实现优先复用已有组件。
- 通过组件预览、测试和代码评审验证结果。
Figma Code Connect连接设计文件中的组件与代码仓库中的实现,有助于确定设计对应哪个组件、需要传哪些属性。完整的业务逻辑仍需另外实现。
评估其他生成工具时,也应检查它是否复用组件、支持内容变化和响应式布局,以及重新生成时如何处理人工修改。静态画面的相似程度不足以判断代码是否适合长期维护。
实施时容易忽略的边界
设计系统需要持续维护。团队要明确组件由谁负责、如何接受新模式,以及何时废弃旧接口。复制组件源码后,如果各处分叉却无人协调,一致性会逐渐消失。
页面组合也需要单独验证。即使组件本身正确,真实内容中的长文本、空数据和权限限制仍可能暴露预览样例没有覆盖的问题,因此应保留少量关键流程的端到端验证。
无障碍评估需要人工参与。WCAG提供可测试要求,但自动工具无法独立判断网站是否满足全部无障碍标准。W3C 明确要求结合人工评估,详见无障碍评估说明。
要判断认知负担是否下降,可以观察开发者完成常见页面时需要多少额外求助、重复样式修复是否减少,以及他们为何绕过公共组件。同时,也要观察用户完成任务时仍卡在哪里。工具文档能解释减负机制,实际效果还需要在项目中验证。
备注:内容仅供参考。
