第一次接触前端 UI 技术栈时,最容易混在一起的是一组看起来都和"界面"有关的名字:Ant Design、Arco Design、Tailwind CSS、shadcn/ui、Radix UI、GSAP、Framer Motion、Magic UI。
它们经常会出现在同一场讨论里,但解决的问题并不一样。如果不先把职责分清楚,很容易把本来可以组合使用的东西当成竞品,也很难判断哪一层真正适合承接公司的设计系统。
我之前在为设计系统组件试点选择技术栈时,就遇到了这个问题。真正需要解决的不是"哪个库最好",而是先回答三个更基础的问题:
这次分类主要由 Claude Code 帮我梳理,我再用自己熟悉的 Figma 组件工作方式去验证这些概念是否成立,并据此做后续技术选型。
把这些工具按职责拆开以后,关系会清楚很多。
| 层级 | 主要职责 | 典型工具 |
|---|---|---|
| 样式基础 | 提供原子化样式能力,由开发者组合出具体视觉 | Tailwind CSS |
| 完整 UI 组件 | 直接提供按钮、输入框、弹窗、表格等已经组装好的界面组件 | Ant Design、Arco Design、shadcn/ui |
| 无样式行为组件 | 处理键盘、焦点、可访问性和交互状态,不规定最终视觉 | Radix UI、Base UI |
| 动效 | 控制元素如何进入、退出、移动和编排 | GSAP、Framer Motion |
| 视觉特效组件 | 提供更偏展示和营销页面的成品视觉效果 | Magic UI |
这里最容易混淆的是 Tailwind 和 UI 组件库。
如果类比到 Figma:
Button / Primary / Large;它们不是同一层的替代关系,而是可以叠加在一起组成完整界面。
这一步是我最早最容易问错的问题。
Tailwind CSS 不提供一个完整的 Button。它提供的是一组原子化 class,让开发者自己决定颜色、间距、圆角、字号和状态,再把这些属性组合成按钮。
UI 组件库则已经把这些东西封装成一个可以直接调用的单位。
放回 Figma 里其实很好理解:设计师从 Auto Layout、Fill、Radius、Spacing 这些基础属性开始搭出一个 Button,然后把它保存成组件。代码世界里也有类似的分层。
shadcn/ui 就是一个很直观的例子。它提供 <Button> 这样的组件,但组件内部的视觉样式本身仍然由 Tailwind class 组成。
所以更准确的关系是:Tailwind 提供样式材料,组件库把这些材料组织成可复用组件。
这个理解一旦建立,"Ant Design 还是 Tailwind"这种问题本身就不再成立,因为两者解决的不是同一层问题。
这次选型里,shadcn/ui 最关键的特点并不是它的默认视觉,而是它的代码归属方式。
Ant Design、Arco 这类传统组件库通常作为依赖安装到项目里。开发者主要通过主题变量和组件 API 去调整它们。
shadcn/ui 的方式不同:通过 CLI 把组件源码直接放进项目仓库。生成以后,这些文件和自己写的代码没有本质区别,可以阅读、修改、删减,也可以逐步替换成公司的设计规范。
对一个需要把 Figma 设计系统转成公司自己的代码组件库 的项目来说,这一点很重要。
我需要的不是"找一套视觉接近公司的现成组件",而是一套已经处理好基础交互和工程结构、但允许我们完全控制最终视觉的组件骨架。
因此,shadcn 更适合作为起点,而不是最终设计语言本身。
继续往下看 shadcn 组件,会发现很多复杂组件并不是从零处理交互状态,而是建立在 Radix UI 这类无样式组件基础上。
这一层负责的是:
但它不负责决定按钮是什么颜色、弹窗是什么圆角。
这和设计系统里的"行为规范"和"视觉规范"是两件事很像。公司可以重新定义视觉 token 和样式,而不必重新发明一套 Dialog 怎么管理焦点、Dropdown 怎么用键盘操作的底层逻辑。
因此,换 Radix 或 Base UI 这类骨架,主要影响内部实现方式,不应该直接决定产品最后长什么样。
Ant Design 和 Arco Design 的优势非常明确:它们已经提供了大量成熟的企业级组件,尤其适合表格、表单、筛选、后台管理等信息密集型产品。
如果目标是快速搭一个中后台,直接采用这种完整组件库通常效率很高。
但如果目标是建立一套视觉上高度独立的公司设计系统,约束也会更明显。组件结构、默认样式和主题机制已经有较强的系统特征,最终要把产品改得完全不像原组件库,需要付出更多覆盖成本。
所以这次没有选择 Ant Design,并不是因为它"不够好",而是它优化的目标和这次项目目标不同。
Arco 对我仍然很有参考价值,因为它本身就同时维护 Figma 设计资源和代码组件,是观察"设计系统如何跨设计和开发同步"的一个成熟案例。
GSAP 和 Framer Motion 也经常和组件技术栈放在一起讨论,但它们不负责组件的基础样式或结构。
它们解决的是元素怎么动。
Framer Motion 更适合常见的 UI 状态变化和页面过渡;GSAP 更擅长复杂时间线和精细动画编排。
因此,一个项目完全可以同时使用 shadcn/ui 和 Framer Motion,也可以在复杂动画场景里再引入 GSAP。这里不存在"选了组件库以后就不需要动效库"的关系。
把这些层级理清以后,我才更准确地理解了 Figma 设计系统和代码组件库之间的关系。
Figma 负责定义:
代码组件还需要处理:
所以目标并不是把 Figma "自动翻译成代码",而是让设计和代码共享同一套设计规则,同时让代码层借用成熟的交互基础设施。
这也是最终采用 React + Tailwind CSS + shadcn/ui 的原因:
实际落地时遇到的一个问题,是 shadcn 默认使用自己的语义变量,例如:
--primary
--muted
--accent而从 Figma 设计系统提取出来的 token 使用的是公司的命名,例如:
--color-text-brand-primary最终没有去逐个修改 shadcn 组件,而是在两套命名之间加一层很薄的映射。
例如让 shadcn 的 --primary 指向公司 token 中对应的变量。
这样做以后:
这层桥接比"把 shadcn 的 token 名直接当成公司的设计 token"更清楚,因为它保留了设计系统自己的语义体系。
最终试点技术栈:React + Tailwind CSS + shadcn/ui。
shadcn 组件源码直接进入项目仓库,而不是只作为不可见的第三方依赖使用。
公司自己的 design token 保持独立命名,通过 CSS variable mapping 对接 shadcn 的语义变量。
复杂组件的行为基础可以继续使用 Radix UI / Base UI 一类无样式 primitive,而最终视觉由公司 token 和组件样式决定。
这次 Claude Code 最有帮助的地方,是把陌生的前端技术生态重新映射到我已经熟悉的 Figma 组件体系里。
但我没有把这个类比直接当成最终结论。比如理解"shadcn 的组件是 Tailwind 拼出来的"之后,我还是打开实际生成的 Button 源码看了一遍,确认它确实是可以阅读和修改的普通组件代码。
对我来说,这种方式比较有效:先用 AI 把陌生概念翻译到已有知识框架里,再用真实代码或产品验证一次。 前一步降低理解成本,后一步确保这个理解足以支撑真正的技术判断。