一、原子设计(Atomic Design)深度解析
核心概念
- 原子(Atoms) :最小的界面单元 ------ 按钮、输入框、颜色块、图标。
- 分子(Molecules) :由原子组合的小组件 ------ 带标签的输入、由图标 + 文本构成的按钮组。
- 有机体(Organisms) :复杂组合,可复用的 UI 区块 ------ 表单、导航栏。
- 模板(Templates) :页面骨架,用占位内容展示结构与布局。
- 页面(Pages) :真实数据驱动的最终页面。
为什么要有原子设计?(底层动机)
- 分离关注点(Separation of concerns) :把样式、可复用性、语义逐层抽象,降低认知复杂度。
- 可组合性:由明确契约(props/API)的最小单元构建更复杂的单位,便于重用与推理。
- 可维护性:当 "原子" 修复或改动,依赖的分子/有机体自动受益。
为什么要这样做?
- 从工程角度:减少重复代码、统一边界条件、便于自动化测试。
- 从设计角度:令牌驱动(tokens)保证视觉一致性。
优点
- 易于推断组件依赖关系与影响面
- 帮助跨团队(设计/前端)沟通,术语一致
- 便于做视觉回归测试和主题切换
限制 / 问题
- 过度拆分会造成抽象过深(组件过多,查找困难)
- 对于性能优化(大量小组件嵌套)需要额外工作 --- 例如避免不必要的重渲染
- 设计与实现契合度需持续维护(设计稿与代码不同步会破坏价值)
如何优化
- 实施组件发现目录(Component Catalog)与可搜索的元数据(tags、usage counts)
- 用组合 API 替代大量微组件(见下文"组件 API 设计")
- 使用自动化工具同步 design tokens(Figma Tokens、Style Dictionary)
示意图:组件层级(Mermaid)
css
flowchart TD
A[Atoms] --> B[Molecules]
B --> C[Organisms]
C --> D[Templates]
D --> E[Pages]
二、组件 API 设计:可组合性、可访问性
A. 可组合性(Composability)
要点
- 最小而完整的契约(props) :每个组件提供必要且语义化的 props,避免 "万能 prop"。
- 容器/呈现分离(Container/Presentational)或 Hook+UI 分离:把逻辑(hooks)与 UI 分开,便于在不同上下文复用。
- 复合(Compound)组件模式 :以父组件作为 API 根,子组件作为声明式插槽(slot) ------ 例如
<Menu><Menu.Button/><Menu.Items/></Menu>
。 - 渲染倾向(render-props / children as function)和插槽(slots) :提供更高的控制权。
为什么这样设计?(底层理由)
- 最小契约 有助于类型推导与文档自动生成(TS 的好处)。
- 逻辑/视图分离 降低组件耦合,便于编写单元测试和状态管理。
- 复合模式 支持高度定制,同时保证内部逻辑(如焦点管理、键盘导航)统一实现。
优点
- 更强的可扩展性和定制能力
- 更易于单元测试和端到端测试(将状态控制与渲染分离)
问题与注意点
- 复合 API 的学习成本比扁平 prop API 更高
- 过度的抽象可能导致类型复杂化(尤其在 TypeScript 下)
示例(概念代码片段 --- 伪代码)
javascript
// Hook + UI 分离
function useToggle(initial=false) { /* 返回状态与控制 API */ }
function ToggleUI({on, onToggle}) { /* 纯 UI */ }
// 使用:
const toggle = useToggle();
<ToggleUI {...toggle} />
B. 可访问性(Accessibility,a11y)
为什么重要
- 合规(法律/企业合规需求)
- 扩大用户群体,降低潜在诉讼风险
- 可访问性约束通常也提升键盘和屏幕阅读器的交互质量
底层原则
- 语义化 HTML :优先使用原生表单控件和语义元素(
<button>
,<input>
,<nav>
)。 - 焦点管理:模态、菜单、对话框必须管理焦点循环与返回聚焦。
- 键盘导航 :提供
Tab
、Enter
、Esc
、箭头键行为空间的支持。 - ARIA 谨慎使用:仅在必要时补充语义,避免错误的 ARIA 模式覆盖原生行为。
常见实践
- 使用无头组件(headless)处理 a11y 复杂行为,例如 Radix 的做法。
- 集成自动化 a11y 测试(axe-core、jest-axe)并纳入 CI。
示例:对话框行为状态机(Mermaid)
sql
stateDiagram-v2
[*] --> closed
closed --> opening : open()
opening --> open : animationEnd
open --> closing : close()
closing --> closed : animationEnd
note right of open: manage focus trap
三、主题系统:颜色、间距、字体、阴影令牌
A. 设计令牌(Design Tokens)概念
- 令牌是抽象化的设计变量:
color.primary
,spacing.4
,font.size.md
,shadow.card
。 - 目的:把视觉系统转成可编程的、可转换的资源,支持跨平台(web、iOS、Android)一致性。
B. 为什么要令牌化?(底层动机)
- 跨平台一致性:同一份 tokens 可以生成多个平台的样式(CSS vars, Android XML, iOS plist)。
- 主题化与皮肤切换:只需替换 tokens 即可完成暗黑/浅色或品牌定制。
- 集中控制,减小变更代价 :修改
color.primary
即影响所有使用处。
C. 令牌组织策略
- 语义化命名优先 :
color.primary
优于color.blue-500
。 - 原子级 vs 语义级:保留低级基础 tokens(色阶、间距刻度)同时提供语义 tokens(button.background)。
- 导出层级:tokens -> css variables -> component styles
D. 实现细节:CSS 变量与 JS tokens
- 在运行时使用 CSS 变量(
--ds-color-primary
)可以让主题切换更即时,且无需重新构建。 - 构建时把 tokens 编译为
.css
,.json
,.scss
等多种格式(推荐使用 Style Dictionary)。
E. 响应式 & 可伸缩间距
- 使用步进间距系统(4、8、12、16...)并提供
spacing.scale
。 - 在组件内部通过
size
或density
props 转化为实际 spacing tokens。
F. 阴影与字重
- 阴影应作为 token 管理(
shadow.card.sm
,shadow.card.lg
),并在不同平台上映射不同实现。 - 字体家族与字重也应作为 tokens 管理,配合替换策略(系统字体或 Web 字体)。
示意图:tokens 流转(Mermaid)
css
flowchart LR
Tokens[Design Tokens JSON] --> Build[Build Tools (Style Dictionary)] --> Outputs[CSS Vars / JSON / Android / iOS]
Outputs --> Components[Component Styles]
四、组件库架构与工程化
A. 包划分(monorepo vs 多仓)
- Monorepo(推荐) :采用 pnpm/workspaces 或 yarn workspaces,提高跨包引用效率,便于同步版本、lint、测试。
- 多仓:适合组织边界非常明确且权限隔离严格的团队。
包结构示例(Monorepo)
packages/tokens
--- design tokenspackages/core
--- 基础原子组件(Button、Input)packages/ui
--- 高级组合组件(Form、Modal)packages/docs
--- Storybook / Docs sitepackages/scripts
--- 构建/发布脚本
B. 构建与发布
- 使用 tsup / rollup 打包 ESModule + CJS + types + CSS exports
- 输出的同时生成 types (
.d.ts
) 和 stylesheet(CSS vars 形式) - 发布到 npm/私服并打 tag(semver + changelog)
C. 文档系统与示例
- 使用 Storybook + MDX,或 Docz,或 VitePress。必须包含:使用示例、API doc、可交互示例、a11y 报告。
- 自动从组件的 TypeScript types 生成 props 表格。
D. 测试策略
- 单元测试:Jest / Vitest(DOM-less)
- 集成/行为测试:React Testing Library
- 视觉回归:Chromatic、Playwright + Percy
- 可访问性测试:axe-core 集成到 CI(对每个 story 运行 a11y 检查)
E. 性能与打包优化
-
Tree-shaking 友好 API(避免导出大对象),确保按需加载。
-
CSS-in-JS vs CSS Modules vs 原子化 CSS:权衡
- CSS vars + 原子化 token-driven CSS 有利于主题切换。
- CSS-in-JS 提供运行时主题能力,但会增加包体积与运行时开销。
F. 发布流水线(CI)示意(Mermaid)
rust
sequenceDiagram
contributor->>git: push PR
git->>CI: run tests
CI->>Registry: publish on merge
Registry->>apps: apps fetch new version
五、流行组件库对比(底层分析)
1) shadcn/ui
- 定位:Tailwind 原子化样式 + 组件集合,便于复制粘贴到项目。
- 为什么存在:Tailwind 使用者希望获得一套通用组件而不牺牲 Tailwind 的可组合性。
- 优点:快速上手,样式透明(类名可见),轻量。
- 问题:紧耦合于 Tailwind;在非 Tailwind 项目中迁移成本高。
- 后续优化:抽象出 tokens 层,允许以 CSS vars 或 Tailwind theme 互换。
2) Radix UI
- 定位:无头(headless)可访问性组件 ------ 仅提供行为与 a11y,外观留给使用方。
- 为什么存在:把复杂的焦点、键盘导航、可访问性交给库实现,UI 自定义由用户完成。
- 优点:极致 a11y、低样式侵入性,适合设计系统底层。
- 问题:开发者需额外实现样式,学习成本稍高。
- 后续优化:提供可选的样式层或官方主题包以降低上手门槛。
3) Chakra UI
- 定位:做有主题系统、样式道具(style props)的组件库。易用、主题驱动。
- 为什么存在:希望在快速搭建产品时拥有一致的主题与直观的样式 API。
- 优点:快速原型、强大的主题定制
- 问题:样式道具会把 API 与样式耦合在一起,部分场景下不利于严格的样式抽象。
- 后续优化:更严格的 tokens 边界与按需加载(减少 bundle)
4) Ant Design
- 定位:企业级、功能全面的组件库(含设计规范)。
- 为什么存在:满足复杂企业后台的丰富需求,包含大量业务场景组件。
- 优点:覆盖广、生态成熟、国际化支持
- 问题:包体积大、主题切换实现复杂(老版本依赖 Less),组件样式/行为对业务适配需要进一步自定义。
- 后续优化:现代化迁移(CSS vars + tokens),按需加载与更小的核心包
六、图示与示意图集合(可直接复制到文档或 Figma)
1) 组件层级架构图(占位 SVG 描述)
-
说明 :中心为
tokens
,向外依次为atoms
->molecules
->organisms
->templates
->pages
。 -
SVG 描述占位:
- 圆环图,中心
tokens
,每层用不同颜色环包裹;在每个层上标注示例组件与典型 props。
- 圆环图,中心
2) 运行时主题流(Mermaid)
shell
flowchart LR
Figma-->|export tokens|TokensRepo
TokensRepo-->|build|CDN
CDN-->|fetch|App
App-->|css vars|RenderedUI
3) 组件渲染与焦点管理(占位示意)
- 说明:对话框打开流程:触发 -> 保存焦点 -> 创建焦点陷阱 -> 关闭时恢复焦点。
- SVG 占位:时序泳道图
七、实战:从 Figma 到 npm 的完整流水线(示例)
- Figma 规范化:使用 Figma Tokens 插件,导出 JSON(颜色、间距、字体、边框)。
- 中央 tokens 仓库 :建立
packages/tokens
,存放 tokens.json,使用 Style Dictionary 进行格式转换。 - 组件开发 :在
packages/core
中实现原子组件,依赖 tokens 包(通过内部包依赖或 CDN during runtime)。 - Storybook 集成:自动从 components 读取 stories,并且在 CI 中运行 visual regression + a11y checks。
- 构建与发布:PR 合并触发 CI,运行测试 -> 构建 -> 发布到 npm/私服。
- 消费方应用:通过 package 指定版本或 tags 拉取,或在运行时拉取 tokens CDN 实现主题切换。
八、最佳实践检查清单(快速上车)
- tokens 已语义化,且有低级基础 token 与语义 token 的映射
- 组件 API 遵循 "逻辑/视图" 分离原则
- 核心 a11y 场景(对话框、菜单、表单控件)均通过自动化检查
- 文档/示例覆盖常见使用场景,并提供暗黑/品牌主题示例
- CI 包含视觉回归与 a11y 检查
- 打包生成 ESM + CJS + types
- 明确 monorepo package 拆分策略与发布流程
九、未来优化方向与研究题目
- 令牌的运行时可替换性:探索如何在不刷新页面的情况下安全替换 tokens(CSS vars + Houdini 的可能性)。
- 自动从设计稿生成组件:研究 Figma→React 的更高保真度自动化(样式合并、语义恢复)。
- 组件行为形式化:使用状态机(XState)为复杂组件(菜单、日历)建模,便于验证和回归测试。
- 跨团队变更影响分析:为每个 token/组件维护使用地图(usage graph),在 PR 时自动提示影响范围。