拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Figma 设计系统发现阶段(Phase 0)实战指南:从代码库 Token 到 Figma 变量的迁移蓝图

Figma 设计系统发现阶段Phase 0实战指南从代码库 Token 到 Figma 变量的迁移蓝图【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills本文基于 figma-generate-library 技能仓库中 discovery-phase.md 参考文档展开。该文档是设计系统构建工作流 Phase 0发现阶段的权威指南定义了在任何写入操作开始之前必须完成的五项任务分析代码库中的 Token、检视 Figma 文件中的既有约定、搜索已订阅的组件库、构建映射计划、解决代码与 Figma 之间的冲突。导读本文是 figma-generate-library 技能中 Phase 0发现阶段的完整实战手册它教会你如何在动手创建任何变量、组件或样式之前从代码库中定位设计 Token 的真实来源用只读的use_figma调用摸清 Figma 文件的既有结构并通过search_design_system建立复用基线。读完本文你将掌握一套先发现、后规划、再写入的规范流程能够输出 Token→变量映射表、组件→组件集映射表与用户确认检查点消息并在代码与 Figma 出现分歧时按既定框架做出不破坏任一方的决策。该技能在 SKILL.md 中把整套设计系统构建编排为 Phase 04 的多阶段工作流跨 20100 次use_figma调用其中 Phase 0 是永远最先执行、且不包含任何写操作的阶段Phase 0: DISCOVERY (always first — no use_figma writes yet) 0a. Analyze codebase → extract tokens, components, naming conventions 0b. Inspect Figma file → pages, variables, components, styles, existing conventions 0c. Search subscribed libraries → use search_design_system for reusable assets 0d. Lock v1 scope → agree on exact token set component list before any creation 0e. Map code → Figma → resolve conflicts (code and Figma disagree ask user) ✋ USER CHECKPOINT: present full plan, await explicit approvalPhase 0 的产出Token 清单、组件清单、差距分析将直接输入 Phase 1 Token 创建 与 Phase 3 组件创建。任何跳过或重排该阶段的行为都会导致后期难以挽回的结构性失败。1. 代码库分析——定位 Token 的真实来源搜索优先级顺序按下列顺序查找 Token 来源找到权威来源后立即停止多种格式可以共存设计 Token 文件*.tokens.json、tokens/*.json、src/tokens/**CSS 变量文件variables.css、tokens.css、theme.css、global.cssTailwind 配置tailwind.config.js、tailwind.config.tsCSS-in-JS 主题对象theme.ts、createTheme、ThemeProvider平台特定来源iOS Asset 目录.xcassets、Androidthemes.xml、colors.xml为什么顺序如此重要Token 会以不同形态散落在代码库各处先找到源头文件如 DTCG 格式的*.tokens.json就能避免从 Style Dictionary 或 Tokens Studio 的生成产物中反向推断也避免从 Tailwind 的bg-blue-500这类工具类名中错误地发明 Token工具类名不是 Token必须从 config 对象中取值。CSS 自定义属性Web 端最常见需要搜索的内容:root { ... } theme { ... } ← Tailwind v4 --color-*, --spacing-*, --radius-*, --shadow-*, --font-*匹配模式/--[\w-]:\s*[^;]/g常见文件位置src/styles/tokens.css、src/styles/variables.css、src/theme/*.css提取与命名转换CSS 属性Figma 变量名Figma 类型WEB 代码语法--color-bg-primary: #fffcolor/bg/primaryCOLORvar(--color-bg-primary)--color-text-secondary: #757575color/text/secondaryCOLORvar(--color-text-secondary)--spacing-sm: 8pxspacing/smFLOATvar(--spacing-sm)--radius-md: 8pxradius/mdFLOATvar(--radius-md)--font-body: Intertypography/body/font-familySTRINGvar(--font-body)命名规则在分类边界处把连字符替换为斜杠路径最后一段内部保留连字符--color-bg-primary→color/bg/primary而--color-bg-primary-hover→color/bg/primary-hover。必须始终把原始 CSS 变量名作为代码语法值存储——绝不从 Figma 变量名推导。如果代码库使用--sds-color-background-brand-default就在setVariableCodeSyntax(WEB, --sds-color-background-brand-default)中使用这个精确字符串。这一点与本仓库 naming-conventions.md 中Figma 变量名与代码名并行存在的规则完全一致Figma 名称是给设计师看的代码语法才是给开发者和 Dev Mode 看的真实标识。Tailwind 配置在tailwind.config.js或tailwind.config.ts中寻找// theme.extend.colors → Figma color 变量 { primary: { DEFAULT: #3366FF, light: #6699FF, dark: #0033CC } } // → color/primary/default, color/primary/light, color/primary/dark // theme.extend.spacing → Figma FLOAT 变量 { xs: 4px, sm: 8px, md: 16px } // → spacing/xs 4, spacing/sm 8, spacing/md 16 // theme.extend.borderRadius → Figma FLOAT 变量 { sm: 4px, md: 8px, lg: 16px } // → radius/sm 4, radius/md 8, radius/lg 16Tailwind 工具类名bg-blue-500、p-4不是 Token——必须从 config 对象中提取值而不是从类名中提取。同时注意 Tailwind v4 的theme块CSS 文件内联定义同样属于此类来源应一并纳入搜索范围。DTCG 格式Design Token Community Group匹配模式*.tokens.json或tokens/*.json。务必定位源文件而非 Style Dictionary 或 Tokens Studio 生成的输出产物。{ color: { bg: { primary: { $type: color, $value: #ffffff }, secondary: { $type: color, $value: #f5f5f5 } } }, spacing: { sm: { $type: dimension, $value: 8px } } }嵌套键直接映射为斜杠分隔的 Figma 名称color.bg.primary→color/bg/primary。$type字段对应 Figma 变量类型color→COLOR、dimension→FLOAT 等$value即该模式的原始值两者都是建库时的直接输入。CSS-in-JS / 主题对象需要搜索createTheme、ThemeProvider、theme {}、styled-components、Emotion、Stitches、vanilla-extract// theme.colors.bg.primary → Figma 变量: color/bg/primary // theme.spacing.sm → Figma 变量: spacing/sm // 多个主题对象 (lightTheme, darkTheme) → 同一 collection 中的 modes对于 Chakra、Ant Design、MUI 这类不使用 CSS 自定义属性的 JS-first 系统代码语法应设置为 JS 属性路径如colors.gray.500、colorPrimary、theme.palette.primary.main而不是 CSS 变量——详见 naming-conventions.md 第 9 节。iOS Token 来源// Asset catalog colors in .xcassets/Colors.xcassets // extension Color { static let bgPrimary Color(bg-primary) } // Look for traitCollection.userInterfaceStyle for dark mode detectionAndroid Token 来源// res/values/colors.xml color nameprimary#3366FF/color // res/values-night/colors.xml (dark mode overrides) // MaterialTheme.colorScheme.primary in Compose // val Primary Color(0xFF3366FF)检测暗色模式平台信号Web (CSS)media (prefers-color-scheme: dark)、.dark { }、[data-themedark]Web (Tailwind)配置中的darkMode: class或darkMode: mediaWeb (JS)与lightTheme并存的独立darkTheme对象iOSColor(uiColor:)搭配traitCollection.userInterfaceStyle、双外观 asset catalogAndroidTheme.*.Night的themes.xml、Compose 中的isSystemInDarkTheme()、values-night/目录Figma 映射规则如果存在暗色模式 → 语义色 collection 至少需要 2 个 modesLight/Dark原始Primitivecollection 保持单模式。这与 token-creation.md 中Primitives1 mode Color semanticLight/Dark的标准架构完全吻合。阴影 / 抬升提取阴影无法成为 Figma 变量——它们将变成Effect Styles。/* 寻找: box-shadow, --shadow-* */ --shadow-sm: 0 1px 2px rgba(0,0,0,0.05); --shadow-md: 0 4px 6px -1px rgba(0,0,0,0.10); --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.10);CSS0 4px 6px -1px rgba(0,0,0,0.1)→ Figma 效果{ type: DROP_SHADOW, offset: {x:0, y:4}, radius: 6, spread: -1, color: {r:0, g:0, b:0, a:0.1} }注意颜色分量在此处已是 0–1 范围Plugin API 要求非 0–255半透明 alpha 直接落在a字段。实际创建 Effect Style 的可执行脚本见 token-creation.md 第 7 节。排版提取代码 Token映射到font-size: 16pxFLOAT 变量scopeFONT_SIZE或 Text StylefontSizeline-height: 1.5Text StylelineHeight: {value: 24, unit: PIXELS}font-weight: 600Text StylefontName: {family: Inter, style: Semi Bold}letter-spacing: -0.02emText StyleletterSpacing: {value: -2, unit: PERCENT}font-family: InterSTRING 变量scopeFONT_FAMILY或 Text StylefontName.family复合文本样式所有属性打包在一起→ Figma Text Styles单个属性 → 带相应 scope 的 Figma 变量。组件提取对每个组件提取名称→ Figma 组件集component set名称联合类型 props→ VARIANT 属性字符串内容 props→ TEXT 属性布尔 props→ BOOLEAN 属性与交互状态组合时 → VARIANT State子节点/插槽 props→ INSTANCE_SWAP 属性// React 示例: interface ButtonProps { size: sm | md | lg; // → VARIANT: Size sm|md|lg variant: primary | secondary; // → VARIANT: Style primary|secondary disabled?: boolean; // → VARIANT: State (combine: default|hover|pressed|disabled) label: string; // → TEXT: Label icon?: ReactNode; // → INSTANCE_SWAP: Icon BOOLEAN: Show Icon } // → Component Set Button变体数量: 3 sizes × 2 styles × 4 states 24变体矩阵爆炸预警本仓库 SKILL.md 与 component-creation.md 都强调若 Size × Style × State 超过 30 种组合应拆分出子组件Building Blocks 模式而不是无节制地扩张变体矩阵。2. Figma 文件检视——只读探查既有约定每次构建开始时都要运行以下use_figma片段。它们全部是只读操作在用户任何检查点之前都可以安全运行。仓库提供的 inspectFileStructure.js 脚本把这些只读探查聚合为一次完整清单返回pages、variableCollections、componentSets、textStyles、effectStyles可以视为本节所有片段的生产级合并版本。列出所有页面(async () { try { const pages figma.root.children.map((p, i) ({ index: i, name: p.name, id: p.id, childCount: p.children.length })); figma.closePlugin(JSON.stringify({ pages })); } catch(e) { figma.closePluginWithFailure(e.toString()); } })();如何解读留意页面命名约定是 PascalCase 还是 sentence case统计分隔页---的数量区分既有的组件页与基础foundations页。列出带 Modes 的变量集合(async () { try { const collections await figma.variables.getLocalVariableCollectionsAsync(); const result collections.map(c ({ id: c.id, name: c.name, modes: c.modes, // [{modeId, name}, ...] variableCount: c.variableIds.length, defaultModeId: c.defaultModeId })); figma.closePlugin(JSON.stringify({ collections: result })); } catch(e) { figma.closePluginWithFailure(e.toString()); } })();如何解读确认是否已存在 primitive/semantic 分层记录 mode 命名是 Light/Dark 还是 SDS Light/SDS Dark通过变量数量判断系统规模——这直接决定采用 token-creation.md 中的简单模式50 tokens、标准模式50–200还是 M3 高级模式200。列出某集合中的变量名称、类型、scope、示例值(async () { try { const collections await figma.variables.getLocalVariableCollectionsAsync(); const targetName Color; // change to the collection you want to inspect const coll collections.find(c c.name targetName); if (!coll) { figma.closePlugin(JSON.stringify({ error: Collection ${targetName} not found })); return; } const allVars await figma.variables.getLocalVariablesAsync(); const vars allVars.filter(v v.variableCollectionId coll.id); const result vars.map(v ({ id: v.id, name: v.name, resolvedType: v.resolvedType, scopes: v.scopes, codeSyntax: v.codeSyntax, // First mode value only, for a sample sampleValue: v.valuesByMode[coll.defaultModeId] })); figma.closePlugin(JSON.stringify({ collection: coll.name, variableCount: result.length, variables: result })); } catch(e) { figma.closePluginWithFailure(e.toString()); } })();如何解读检查变量是否使用了ALL_SCOPES违反最佳实践应立即标记检查命名约定是否斜杠分层检查 code syntax 是否已设置识别别名链alias chains。列出带属性的组件集(async () { try { await figma.setCurrentPageAsync(figma.currentPage); // ensures page context const componentSets figma.currentPage.findAll(n n.type COMPONENT_SET); const result componentSets.map(cs ({ id: cs.id, name: cs.name, variantCount: cs.children.length, properties: Object.entries(cs.componentPropertyDefinitions).map(([key, def]) ({ name: key, type: def.type, variantOptions: def.variantOptions || null, defaultValue: def.defaultValue })) })); figma.closePlugin(JSON.stringify({ componentSets: result, count: result.length })); } catch(e) { figma.closePluginWithFailure(e.toString()); } })();注意要搜索所有页面请遍历figma.root.children并对每个页面调用setCurrentPageAsync——这正是 inspectFileStructure.js 内部实现的做法它还会额外捕获不在组件集内的独立组件COMPONENT且父节点不是COMPONENT_SET。列出所有样式(async () { try { const [textStyles, effectStyles, paintStyles] await Promise.all([ figma.getLocalTextStylesAsync(), figma.getLocalEffectStylesAsync(), figma.getLocalPaintStylesAsync() ]); figma.closePlugin(JSON.stringify({ textStyles: textStyles.map(s ({ id: s.id, name: s.name, fontSize: s.fontSize, fontName: s.fontName })), effectStyles: effectStyles.map(s ({ id: s.id, name: s.name, effectCount: s.effects.length })), paintStyles: paintStyles.map(s ({ id: s.id, name: s.name })), counts: { text: textStyles.length, effect: effectStyles.length, paint: paintStyles.length } })); } catch(e) { figma.closePluginWithFailure(e.toString()); } })();检查既有组件上的命名约定(async () { try { // Replace with the node ID of an existing component to analyze const node await figma.getNodeByIdAsync(YOUR_NODE_ID); if (!node) { figma.closePlugin(JSON.stringify({ error: Node not found })); return; } // Check fills for variable bindings const fillInfo []; if (fills in node Array.isArray(node.fills)) { for (const fill of node.fills) { if (fill.type SOLID fill.boundVariables?.color) { fillInfo.push({ type: variable_alias, id: fill.boundVariables.color.id }); } else if (fill.type SOLID) { fillInfo.push({ type: hardcoded, r: fill.color.r, g: fill.color.g, b: fill.color.b }); } } } figma.closePlugin(JSON.stringify({ name: node.name, type: node.type, fills: fillInfo, pluginData: node.getPluginData(dsb_key) || null })); } catch(e) { figma.closePluginWithFailure(e.toString()); } })();如何解读该片段区分variable_alias已绑定变量的填充记录其变量 ID与hardcoded硬编码颜色值——这是判断既有组件是否符合视觉属性全部绑定变量标准的直接证据。注意其中读取的dsb_key是 figma-generate-library 自身的 idempotency 标记键详见 error-recovery.md在发现阶段可用于识别本技能此前构建的节点。3. 使用 search_design_system 建立复用基线它搜索什么search_design_system针对给定文件执行三路并行搜索范围是已订阅的设计库Components—— 已发布的库组件通过推荐引擎按名称/描述搜索相关性排序非精确匹配Variables—— 跨已订阅库的设计 Token颜色、间距等Styles—— paint styles、text styles、effect styles只有文件已订阅的库会被搜索。如果结果为空说明该文件可能未订阅任何设计系统库。输入参数search_design_system({ query: button, // required — text query fileKey: abc123, // required — your file key includeComponents: true, // default true includeVariables: true, // default true includeStyles: true // default true })返回值{ components: [ { name: Button, libraryName: Design System, assetType: component_set, componentKey: abc123def, description: Primary action button } ], variables: [ { name: colors/primary/500, variableType: COLOR, variableSetKey: set1key, key: var1key, scopes: [FILL_COLOR], variableCollectionName: Colors } ], styles: [ { name: Heading/H1, styleType: TEXT, key: style1key } ] }如何解读结果ComponentscomponentKey可在use_figma中用于导入组件const component await figma.importComponentByKeyAsync(abc123def); // or for component sets: const componentSet await figma.importComponentSetByKeyAsync(abc123def);VariablesvariableSetKey是 collection 的 keykey是变量的 key。用它们理解既有命名约定以及有哪些 Token 可供别名alias引用。Styleskey可直接配合figma.importStyleByKeyAsync(key)导入当前文件。何时搜索Phase 0 步骤 0c在规划任何内容之前进行宽泛搜索query: button、query: color、query: spacing。这确立了复用基线。每个组件创建前一刻在写任何use_figma创建代码之前搜索具体的组件名。复用决策条件决策找到变体 API 匹配、Token 模型相同的组件导入并复用找到组件但变体属性错误或有硬编码值重建找到视觉匹配但 API 不兼容的组件包装作为嵌套实例放进新的包装组件内本仓库 SKILL.md 第 5 节给出了同款决策矩阵的完整判定条件API 匹配、Token 绑定模型兼容、命名约定一致、组件可编辑且不属于不归自己所有的远程库并定义了总优先级本地既有 → 已订阅库导入 → 新建。4. 构建计划——在写入前锁定范围完成代码库分析与 Figma 检视后产出映射表并提交给用户。Token → 变量映射表为代码中找到的每个 Token 记录代码 TokenCSS 名称原始值Figma CollectionFigma 变量名Figma 类型Mode(s)theme.colors.blue[500]--color-blue-500#3B82F6Primitivesblue/500COLORValuetheme.colors.bg.primary--color-bg-primary(light: blue/50, dark: gray/900)Colorcolor/bg/primaryCOLORLight, Darktheme.spacing.sm--spacing-sm8pxSpacingspacing/smFLOATValuetheme.radii.md--radius-md8pxSpacingradius/mdFLOATValuetheme.shadows.md--shadow-md0 4px 6px rgba(0,0,0,0.1)——Effect Style—组件 → 组件集映射表代码组件Props → 变体轴变体数量Figma 页面复用?Buttonsize (sm/md/lg) × variant (primary/secondary) × state (default/hover/disabled)18Buttons先搜索Avatarsize (sm/md/lg) × type (image/initials/icon)9Avatars先搜索差距识别对比代码中发现的内容与 Figma 中已存在的内容新建New代码中存在但 Figma 中没有的 Token 或组件 → 创建已存在ExistingFigma 中已有同名 Token 或组件 → 验证 scope / code syntax跳过或更新冲突Conflict同名但值不同 → 升级给用户决策见第 5 节Figma 独有Figma-onlyFigma 中存在但代码中没有 → 标记给用户通常跳过用户检查点消息模板继续执行前必须呈现此消息。未获用户明确批准绝不进入 Phase 1。Heres what I found and what I plan to build: CODEBASE ANALYSIS Colors: {N} primitives ({families}), {M} semantic tokens ({light/dark if applicable}) Spacing: {N} tokens ({range}) Typography: {N} text styles, {M} individual scale tokens Shadows: {N} levels → will become Effect Styles Components: {list of component names} EXISTING FIGMA FILE Collections: {N} existing collections Variables: {M} existing variables Styles: {K} text, {L} effect, {J} paint styles Components: {list} PLAN New collections: {list with mode counts} New variables: ~{N} ({breakdown by collection}) New styles: {N} text, {M} effect New components: {list} Libraries to search before each component: {list} GAPS / CONFLICTS NEEDING DECISIONS ⚠ {conflict description} — Code says X, Figma already has Y. Which wins? WHAT I WONT BUILD (and why) - {item}: already exists in Figma with matching conventions - {item}: not supported as a Figma variable (e.g. z-index, animation timing) Shall I proceed?检查点机制是整个工作流的硬性要求。SKILL.md 明确列出 7 个强制检查点发现范围锁定、基础层、文件结构、每个组件、每个冲突、最终 QA并特别强调looks good 不构成对下一阶段的批准——在检查点处必须明确说出下一阶段名称。5. 冲突解决——当代码与 Figma 意见不一致时当同一 Token/组件在代码和 Figma 中同时存在但值、名称或结构不同时务必询问用户。绝不静默选择一方。决策框架场景询问用户相同的 CSS 名称、不同的 hex 值如代码中--color-accent是#3366FFFigma 中是#5B7FFF代码是#3366FFFigma 中color/accent/default目前是#5B7FFF。哪个是正确的相同组件名、不同变体轴代码有size: sm/md/lgFigma 有Size: Small/Large代码用 3 种尺寸sm/md/lg但 Figma 只有 2 种Small/Large。我应该新增 Medium还是改名以匹配代码代码有语义 Token 但没有 primitive 层Figma 已有完整分层系统代码库采用扁平的单词 Token 模型Figma 文件使用 primitive/semantic 分层。我应该匹配 Figma 架构还是代码架构Figma 变量已存在但使用ALL_SCOPES违反最佳实践我发现color/bg/primary已存在但它使用 ALL_SCOPES。我建议改为FRAME_FILL, SHAPE_FILL。我可以更新 scope 吗代码用 camelCasebackgroundColorFigma 用斜杠分层color/bg/default代码库使用 camelCase 命名Figma 文件使用斜杠分层。对于新变量我是否应该使用斜杠分层Figma 标准并通过 code syntax 映射代码胜出Code Wins默认以代码为真值来源的情况Hex 值代码是线上生产值Token 命名CSS 变量名会成为 code syntaxMode 值light/dark 划分来自代码Figma 胜出Figma Wins默认以 Figma 为真值来源的情况Collection 架构如果已存在结构良好的系统扩展它而非替换它变量命名层级如果设计师已经在用特定名称使用该系统页面结构匹配既有页面组织模式两者都不占优协商当任何一方都不明显正确时提出解决方案并询问我建议 [方案]。这样代码 Token 名称和 Figma 命名约定都能保留。可以吗冲突解决后的落地要点从仓库实现角度看冲突解决结论应同步反映在 naming-conventions.md 的并行标识系统规则中Figma 名称与代码标识code syntax、Code Connect source path是两个平行体系冲突的常见化解方式就是Figma 保留人类可读名称 code syntax 携带精确 CSS 名称。此外整个冲突决策过程应记录进状态账本state ledger配合 rehydrateState.js 与 error-recovery.md 中基于dsb_run_id/dsb_key的标记体系保证长工作流中断后可按{key → nodeId}映射重建现场、幂等续跑。结语为什么发现阶段值得认真对待discovery-phase.md 反复强调一个核心主张设计系统构建绝非一次性任务而发现阶段是唯一保证后续 20–100 次写入调用不跑偏的前置投资。代码库分析回答有什么Figma 检视回答已有什么search_design_system回答能复用什么映射表回答要建什么冲突解决回答谁说了算。只有这五步全部完成并经过用户检查点批准才轮到 token-creation.mdPhase 1和 component-creation.mdPhase 3登场。把 Phase 0 做扎实后续的变量绑定、变体矩阵与文档页创建才能建立在可验证、可恢复的确定性之上。【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门