每个 figmascope 导出都包含 tokens.json。它是 Figma 视觉值与你的代码所需的类型化常量之间的桥梁。本文涵盖 schema、键如何命名、当 Figma 文件没有 Variables 时会发生什么,以及令牌系统诚实的局限所在。
Schema
顶层结构有四个部分:
{
"spacing": {
"spacing.4": { "$value": 4, "$type": "dimension" },
"spacing.8": { "$value": 8, "$type": "dimension" },
"spacing.12": { "$value": 12, "$type": "dimension" },
"spacing.16": { "$value": 16, "$type": "dimension" },
"spacing.24": { "$value": 24, "$type": "dimension" }
},
"radius": {
"radius.4": { "$value": 4, "$type": "dimension" },
"radius.8": { "$value": 8, "$type": "dimension" },
"radius.12": { "$value": 12, "$type": "dimension" }
},
"color": {
"color.7f5cfe": { "$value": "#7f5cfe", "$type": "color" },
"color.ffffff": { "$value": "#ffffff", "$type": "color" },
"color.1a1a2e": { "$value": "#1a1a2e", "$type": "color" }
},
"typography": {}
}
格式类似 W3C 设计令牌社区组标准:每个令牌是一个带有 $value 和 $type 的对象。这不是严格的 W3C DTCG 实现——figmascope 早于最终规范发布,且不实现像 fontFamily 这样的复合类型——但足够接近,使得 DTCG 感知工具可以在略微适配后解析它。
空的 typography 对象不是错误。下面会介绍。
值派生键命名
当 Figma 文件有 Variables 时,令牌键来自设计师设置的 Variable 名称。spacing.md、color.brand.primary,无论设计系统使用什么。
当 Figma 文件没有 Variables——这是现实世界中大多数 Figma 文件的情况——figmascope 回退到值派生命名。间距值 16 变为 spacing.16。颜色 #7f5cfe 变为 color.7f5cfe。圆角半径 4 变为 radius.4。
这是一个深思熟虑的权衡。值派生名称不好看但稳定。它们从实际值派生,所以相同 Figma 文件的两次不同运行产生相同的键。spacing.16 始终意味着 16dp。Agent 可以依赖它。
替代方案是像 spacing.1、spacing.2 这样的位置名称。那些是脆弱的——添加一个更小的间距值,所有东西都会移位。值派生名称不会移位。
值派生名称是没有 Variables 但应该有的文件的后备方案。如果你的设计系统有 40 个间距值都需要语义名称,推动设计师设置 Variables。推断后备存在于现实世界文件,而不是作为真实令牌系统的替代品。你可以在自己的文件上运行 figmascope 来查看它采用哪种溯源路径。
tokensSource 字段及其含义
_meta.json 包含一个 tokensSource 字段,告诉你令牌是如何派生的:
| 值 | 含义 |
|---|---|
"figma-variables" |
Figma 文件有 Variables 且直接使用了它们。令牌名称是设计师指定的。完整覆盖。 |
"inferred-from-frequency" |
没有 Variables。figmascope 扫描所有节点,找到频繁出现的值,将其提升为令牌。覆盖率取决于设计的一致性。 |
"none" |
没有 Variables,推断也没有产生有用的结果。tokens.json 将有空或接近空的部分。 |
_meta.json 中的 "tokens-inferred-from-frequency" 警告与此对应。如果你看到它,令牌覆盖率是尽力而为的。
当 tokensSource 是 "inferred-from-frequency" 时,推断算法是:找到所有在三个或更多节点的 padding、gap 或 cornerRadius 字段中出现的尺寸值。将这些分别提升为间距或圆角令牌。对填充颜色执行相同操作。只出现一两次的值被视为一次性,不提升。
这对内部一致的设计效果很好。对间距自由变化的探索性设计效果很差。_meta.json 警告的存在正是让 Agent 知道它处于哪种情况。
为什么排版通常为空
排版令牌需要类型为 FLOAT 或 STRING 的 Figma Variables 才能可靠提取。Figma 中的文本样式作为共享样式存在,而不是 Variables,样式的 API 接口与 Variables API 不同。
figmascope v0.4 在 Variables 覆盖时提取排版。它不尝试基于频率的排版推断,因为有用的排版令牌——字体族、行高、字间距、字重组合——没有像 spacing.16 那样明显的值派生名称。fontSize.14 键比 typography.body.small 有用得多,而生成一个不好的名称比不生成名称更糟糕。
所以结果是诚实的:如果你的 Figma 文件有排版 Variables,你会得到排版令牌。如果没有,你会得到一个空对象,Agent 通过 CONTEXT.md 被告知排版覆盖可能是部分的。
// _meta.json
{
"tokensSource": "inferred-from-frequency",
"warnings": [
"tokens-inferred-from-frequency"
]
}
Agent 看到这个,知道对排版令牌引用要保守。它生成带有明确值的后备代码和 TODO 注释,而不是发明令牌名称。
Agent 如何使用 tokens.json
CONTEXT.md 约束是:"如果令牌存在于 ±2dp 范围内,永远不要硬编码 dp 值。"±2dp 容差处理舍入。如果节点有 paddingLeft: 15 且 spacing.16 存在,Agent 使用 spacing.16。如果最接近的令牌是 spacing.24,不匹配——Agent 使用字面值。
对于颜色,匹配在归一化为 6 位小写后对十六进制值精确匹配。#7F5CFE 匹配 color.7f5cfe。
对于圆角半径,与间距相同的 ±2dp 规则。
Jetpack Compose 目标的实际输出如下所示:
// tokensSource 为 figma-variables
Surface(
shape = RoundedCornerShape(Radius.radius8),
color = Color.Brand.Primary
) {
Column(
verticalArrangement = Arrangement.spacedBy(Spacing.spacing16),
modifier = Modifier.padding(horizontal = Spacing.spacing24)
) { ... }
}
// tokensSource 为 inferred-from-frequency(相同的视觉输出,相同的令牌引用)
Surface(
shape = RoundedCornerShape(Radius.radius8),
color = Color.color7f5cfe
) { ... }
值派生名称可读性较差。它们仍然优于硬编码值。
与其他令牌提取方式的比较
Figma 的原生"检查"面板按节点显示值。没有导出的令牌文件。你必须手动创建一个,或使用像 Tokens Studio 这样的插件。两者都需要设计师的努力和持续维护。
figmascope 在每次导出时自动提取令牌。如果文件更改,重新导出,令牌反映当前状态。权衡是,没有 Variables 时名称是值派生的而非语义的——但你每次都会得到一个令牌文件,不需要插件或额外的工作流步骤。
Tokens Studio(前身为 Style Dictionary 集成)需要设计师设置插件、在 Figma 文件中维护 JSON 文件并同步它。这是成熟设计系统的正确解决方案。figmascope 的方式是尚未达到那个阶段的文件的正确解决方案,这是大多数文件。
令牌提取是文件中存在内容的尽力快照。它不是令牌优先设计系统的替代品。它是你在朝着那个方向构建时使用的东西。
将 tokens.json 连接到你的代码库
JSON 结构足够扁平,从中生成 Kotlin 对象或 TypeScript 模块很简单。一个简单的脚本:
// tokens.json → Kotlin 对象(简化)
const tokens = JSON.parse(fs.readFileSync('tokens.json', 'utf-8'));
let output = 'object Spacing {\n';
for (const [key, token] of Object.entries(tokens.spacing)) {
const name = key.replace('spacing.', 'spacing').replace('.', '_');
output += ` val ${name} = ${token.$value}.dp\n`;
}
output += '}';
// → val spacing16 = 16.dp
如果你已经使用设计令牌管道,W3C 风格格式足够接近,可以通过自定义 $value/$type 键的转换器直接放入 Style Dictionary。
令牌如何与 IR 交互的其余部分在 每屏 IR 中介绍。令牌如何与更广泛的 Agent 上下文交互,请参阅 CONTEXT.md 解析。要从你自己的 Figma 文件导出令牌,前往 figmascope.dev。