← ALL NOTES
METHOD·2026.09·5 MIN READ
从 Figma 组件属性到代码 Props
figma-apidesign-systems

这篇记录把 Figma 组件落到代码组件时,我现在会怎么处理 componentPropertyDefinitions 里的属性。

Figma 里的 Variant、Boolean、INSTANCE_SWAP 都是在设计编辑器里帮助设计师维护和预览组件的。代码组件要面对的是运行时状态、真实交互和 API 设计。两边有大量对应关系,也会出现一些只存在于 Figma 编辑环境里的设置。

我现在不会直接把 Figma 属性一项项翻成 Props,而是先看每个属性在真实产品里代表什么。

浏览器自己知道的状态,不需要再做一层 Prop

Figma 很常见的一种做法,是用 Status Variant 把 Default / Hover / Focus / Selected 全部摆出来。这样设计师可以在画布里一次看全所有状态。

代码里,Hover、Focus、Disabled 这类状态通常直接交给浏览器和 CSS:

CSS
:hover
:focus-visible
:disabled

例如 List - Select 在 Figma 里有 Default / Hover / Focus / Selected。落到代码以后,我会这样处理:Hover → :hover;Focus → :focus-visible;Selected → selected Prop。

Selected 仍然需要由业务状态明确传入,因为浏览器不会自动知道某一项是不是当前选中项。

这里需要看真实交互语义,不能只按 Variant 名字映射。之前一个 Select 组件里的 Focus,实际表达的是下拉面板已经展开。这个状态应该跟 open 一类运行时状态绑定,而不是直接套 :focus-visible。

所以检查 Variant 时,我会先确认:这个状态是谁产生的?浏览器是否已经能感知?它代表视觉焦点、业务选中,还是组件展开?

名字只能作为线索,最终还是看状态在产品里的实际含义。

INSTANCE_SWAP 在代码里通常就是插槽

Figma 的 INSTANCE_SWAP 用来替换组件里的图标或子组件。

例如 List - Select 的 Leading Icon 可以在 Figma 右侧面板里换成任意图标实例。

代码里不需要保留"实例交换"这个编辑器机制,通常直接提供一个可选的插槽参数:

TYPESCRIPT
<ListSelect leadingIcon={<SearchIcon />} />

具体使用 React Element、Component Type 还是 slot API,可以根据代码规范决定。这里真正需要保留的是"这个位置允许传入一个图标或子组件"。

一个 Variant 同时控制几件事时,先拆语义

Figma 里的 Variant 轴越多,组件集需要维护的组合就越多。设计阶段经常会把几件一起出现的变化收在一个 Variant 里。

Group - Input with Btn 就遇到过这种情况。Figma 只有一个 Type:

Normal
Icon

但这个 Variant 同时改变了两件事:内容区是否可编辑;右侧按钮使用文字还是图标。

这两件事在运行时没有必然绑定关系。如果代码继续只做一个 type="normal | icon",组件只能支持设计稿里已经画出的两种组合。

开发侧拆成两个独立维度以后,组合会更完整,例如:

TYPESCRIPT
contentMode="editable | readonly"
buttonMode="text | icon"

这样可以自然得到四种组合。

我现在看到一个 Variant 时,会观察切换它以后哪些东西一起发生变化。如果同时改变了多种性质不同的行为或内容,就继续判断这些变化在真实产品里是否需要始终绑定。

Figma Variant 更适合表达设计样本;代码 API 需要表达可以独立变化的运行时维度。

共享 Boolean 要逐个 Variant 检查绑定内容

Boolean Property 是另一个容易误判的地方。

Figma 的 Boolean 可以挂在整个 Component Set 上,所以不同 Variant 都会看到同一个开关。每个 Variant 内部可以把这个开关接到不同图层上,Figma 不会因为语义变化而报错。

FormField 里有一个 Show Required:Input / Textarea Variant 控制必填星号;Tag Variant 控制一条"添加成员"的操作链接。

属性名字看起来完全一致,实际控制的是两类不同内容。

这种情况如果直接生成:

TYPESCRIPT
showRequired={true}

Tag Variant 的 API 就会变得很奇怪,因为这个 Prop 已经失去原来的含义。

我现在会逐个 Variant 检查 Boolean 实际绑定的图层和功能。发现语义不一致时,常见处理有几种:拆成两个更明确的 Props;某个 Variant 不暴露这个 Prop;如果 Variant 本身已经代表完全不同的组件职责,直接拆组件。

代码层的命名最好能稳定描述运行时含义,不需要维持 Figma Component Set 表面上的属性统一。

Variant 值到底是 enum,还是几个独立开关

Figma 为了把组合摆开,经常会做完整或半完整的 Variant 矩阵。

代码实现时,我会先看这些值之间的关系。

同一时刻只能成立一个,保留成 enum:

TYPESCRIPT
size="sm | md | lg"
orientation="horizontal | vertical"

多个状态可以同时成立,拆成独立 Boolean:

TYPESCRIPT
selected
highlighted
removable

即使 Figma 里只画了其中几个组合,代码也不需要把这些状态重新绑回一个大 Variant。

反过来,如果 Figma 里把三个互斥状态拆成三个 Boolean,代码也没有必要照搬成 isA / isB / isC。这种写法容易产生同时为 true 的非法组合。运行时互斥的状态更适合一个 enum 或明确的 state value。

我通常会先列一下这些状态在运行时能不能同时成立,再决定 API 形态。

Storybook 的静态预览可以单独处理

组件文档里经常需要把 Hover、Focus 之类的状态静态摆出来。

真实组件仍然可以继续使用 CSS 原生状态。展示页单独增加演示用开关即可,例如:

TYPESCRIPT
forceHover
forceFocus

这些开关只用于 Storybook 或组件文档:不进入正式的公开 API 文档;不放进真实业务使用的 Playground;不参与产品状态管理。

这样既能保留完整的视觉对比页,也不会为了截图和文档展示给正式组件增加一组长期维护的假状态。

这类展示能力最好和真实运行时 API 分开看。Storybook 需要"摆出来",产品代码需要"真的发生"。

我现在会怎么检查一个 Figma 组件

拿到一个新的 Component Set,我通常按下面这个顺序过一遍。

  1. 先看 Variant。 检查每个 Variant 表达的是视觉规格、浏览器状态、业务状态、内容模式,还是组件模式。先把性质分开,再决定是 CSS、enum 还是独立 Prop。
  2. 再看 Boolean。 逐个确认它实际控制什么图层。跨 Variant 共用的 Boolean 尤其需要检查,不能只看属性名。
  3. 看 INSTANCE_SWAP。 通常转换成 icon、slot、leading、trailing、children 这一类内容插槽。
  4. 检查 Variant 有没有捆绑多个维度。 如果一个 Variant 同时控制内容、行为、图标、可编辑性等多个方面,逐项确认哪些需要拆开。
  5. 检查状态之间是否可以共存。 互斥 → enum / 单一 state;可叠加 → 独立 Boolean。
  6. 把展示层需求单独列出来。 Hover、Focus 等静态预览需求,可以留给 Storybook 的专用参数,不进入正式组件 Props。
一份简化的映射表
Figma 属性代码里常见处理
Hover / Focus / Disabled VariantCSS / DOM 原生状态
业务 Selected / Open 状态Boolean 或受控状态 Prop
互斥 Variantenum Prop
可叠加状态多个独立 Boolean Props
INSTANCE_SWAPicon / slot / child component
Figma 为减少组合数而耦合的 Variant按运行时语义拆成多个 Props
跨 Variant 语义不一致的 Boolean拆 Prop、限制适用范围,必要时拆组件
静态 Hover / Focus 预览Storybook 专用 force state

Figma 和代码组件最终都在描述同一个产品组件,但两边服务的环境不同。Figma 需要方便设计师预览、切换和维护样本;代码需要保证状态来源清楚、组合合法、API 好用。

我现在更习惯先把 Figma 属性当成一份设计侧的信息输入,再根据运行时语义整理出代码 Props。这样生成出来的组件 API 会更接近产品真实行为,也能减少为了还原 Figma 编辑器结构留下来的冗余状态。

CONTINUE READING
Figma 设计系统的工程化落地2026.09动态替换 3D 模型时的事件绑定失效2026.09