Дизайн-токены — это общий словарь между дизайнерами и разработчиками. Они существуют в разных формах ещё со времён Salesforce Lightning — Style Dictionary, Theo, черновик спецификации W3C Design Token Community Group. Что изменилось в последнее время — они стали также общим словарём между вашей кодовой базой и AI-агентами для написания кода.
Агент, знающий, что color.accent = #d96a3a, будет использовать именно это значение. Агент, получивший скриншот, угадает «тёплый оранжевый» и выдаст что-то близкое, но неверное. Эта ошибка накапливается на десятках компонентов.
figmascope экспортирует tokens.json как часть каждого бандла контекста. В этой статье подробно объясняется формат, как figmascope извлекает токены из Figma, резервный механизм вывода по частоте для файлов без Figma Variables, и как подключить результат к распространённым фреймворкам. Чтобы увидеть, как токены вписываются в полный рабочий процесс экспорта, посетите приложение figmascope или прочитайте Figma и Cursor и Figma и Claude Code.
Формат tokens.json
figmascope использует формат, вдохновлённый спецификацией W3C Design Token Community Group. Каждый токен — объект с полями $value и $type, вложенный под ключи семантических категорий:
{
"color": {
"surface": { "$value": "#f6f2ea", "$type": "color" },
"surface-2": { "$value": "#efe9dc", "$type": "color" },
"ink": { "$value": "#1f1d1a", "$type": "color" },
"ink-muted": { "$value": "#4a4641", "$type": "color" },
"accent": { "$value": "#d96a3a", "$type": "color" },
"accent-soft": { "$value": "#f2c7a8", "$type": "color" },
"good": { "$value": "#6a8f5a", "$type": "color" },
"warn": { "$value": "#c89a3a", "$type": "color" },
"bad": { "$value": "#b8553a", "$type": "color" }
},
"spacing": {
"1": { "$value": "4px", "$type": "dimension" },
"2": { "$value": "8px", "$type": "dimension" },
"3": { "$value": "12px", "$type": "dimension" },
"4": { "$value": "16px", "$type": "dimension" },
"6": { "$value": "24px", "$type": "dimension" },
"8": { "$value": "32px", "$type": "dimension" },
"12": { "$value": "48px", "$type": "dimension" },
"16": { "$value": "64px", "$type": "dimension" }
},
"radius": {
"sm": { "$value": "4px", "$type": "dimension" },
"md": { "$value": "8px", "$type": "dimension" },
"lg": { "$value": "16px", "$type": "dimension" },
"full": { "$value": "9999px", "$type": "dimension" }
},
"typography": {
"body": {
"fontFamily": { "$value": "Inter", "$type": "fontFamily" },
"fontSize": { "$value": "14px", "$type": "dimension" },
"fontWeight": { "$value": 400, "$type": "number" },
"lineHeight": { "$value": 1.45, "$type": "number" }
},
"heading": {
"sm": {
"fontFamily": { "$value": "Inter", "$type": "fontFamily" },
"fontSize": { "$value": "18px", "$type": "dimension" },
"fontWeight": { "$value": 600, "$type": "number" },
"lineHeight": { "$value": 1.25, "$type": "number" }
}
},
"mono": {
"fontFamily": { "$value": "JetBrains Mono", "$type": "fontFamily" },
"fontSize": { "$value": "13px", "$type": "dimension" },
"fontWeight": { "$value": 400, "$type": "number" }
}
}
}
Почему «W3C-образный», а не строго по спецификации?
Спецификация W3C Design Token Community Group всё ещё находится в статусе черновика. figmascope следует основным соглашениям ($value, $type, вложенные группы), но не реализует все крайние случаи, такие как составные типы и цепочки разрешения псевдонимов. Вывод достаточно стабилен для потребления с помощью Style Dictionary, прямого обхода JSON или языковой моделью — которая является основным потребителем.
Используемые типы токенов
| $type | Категория | Формат $value | Пример |
|---|---|---|---|
color |
цвет | hex-строка | "#d96a3a" |
dimension |
отступы, радиус | CSS-строка с единицей измерения | "16px" |
fontFamily |
типографика | строка с названием шрифта | "Inter" |
number |
типографика | число без единиц измерения | 400, 1.45 |
Цвета всегда выводятся как hex-строки. Если источник в Figma использует RGBA с неполной прозрачностью, альфа-канал сохраняется в 8-значном hex (#d96a3a80).
Как figmascope извлекает токены
figmascope использует двухуровневую стратегию извлечения. Figma Variables — предпочтительный источник; вывод по частоте — резервный вариант.
Уровень 1: Figma Variables
Если в Figma-файле определены Variables (нативная система токенов Figma, доступная на тарифах Professional и Organization), figmascope читает их через эндпоинт /v1/files/:key/variables/local REST API. Имена переменных используются как ключи токенов, приведённые к kebab-case. Режимы сворачиваются к режиму по умолчанию, если только файл не имеет единственного режима — тогда значения этого режима используются напрямую.
Коллекции переменных отображаются в ключи категорий верхнего уровня в tokens.json. Коллекция с именем «Color» порождает tokens.color.*; «Spacing» порождает tokens.spacing.*. Если ваш Figma-файл использует нестандартные имена коллекций, figmascope пытается вывести категорию из разрешённого типа переменной.
Уровень 2: Вывод по частоте
Многие Figma-файлы — особенно старые или файлы от клиентов, не перешедших на Variables — не имеют определений переменных. figmascope справляется с этим, обходя всё дерево узлов и строя гистограммы частот для цветов заливки, значений отступов, радиусов скругления и свойств типографики.
Значения, встречающиеся выше порогового значения частоты, становятся кандидатами в токены. Они именуются по категории и порядковому индексу (color.0, color.1...), если только из контекста использования нельзя вывести читаемое имя (например, цвет, используемый только на фонах нескольких фреймов, получит имя color.surface).
Манифест _meta.json записывает значение tokensSource как "variables" или "inferred", чтобы промпт агента мог учитывать случай вывода:
// _meta.json — inferred fallback
{
"tokensSource": "inferred",
"warnings": [
{
"code": "tokens-inferred",
"message": "No Figma Variables found. Tokens were inferred from usage frequency."
}
]
}
Выведенные токены полезны, но не авторитетны. Рассматривайте их как отправную точку, а не как спецификацию дизайн-системы.
Ссылки на токены в IR разметки
Файлы IR для каждого экрана (screens/*.json) ссылаются на токены по пути через строки $ref, а не встраивают сырые значения. Это делает IR компактным и гарантирует, что агент всегда разрешает значения из единственного источника истины:
{
"kind": "stack",
"name": "PrimaryButton",
"direction": "horizontal",
"gap": { "$ref": "spacing.2" },
"padding": {
"top": 10, "right": 16, "bottom": 10, "left": 16
},
"background": { "$ref": "color.accent" },
"radius": { "$ref": "radius.sm" },
"children": [
{
"kind": "leaf",
"name": "ButtonLabel",
"type": "text",
"style": { "$ref": "typography.body" },
"color": { "$ref": "color.surface" }
}
]
}
Агент, обрабатывающий этот узел, разрешает color.accent в #d96a3a и spacing.2 в 8px из параллельного файла tokens.json. Никакой двусмысленности, никаких галлюцинаций значений.
Полная документация по схеме узлов описана в статье per-screen IR explained.
Использование tokens.json со Style Dictionary
Вывод figmascope совместим с Style Dictionary с минимальной конфигурацией. Поскольку он уже использует соглашение $value / $type, можно направить Style Dictionary прямо на tokens.json:
// style-dictionary.config.js
module.exports = {
source: ['design/tokens.json'],
platforms: {
css: {
transformGroup: 'css',
prefix: 'fs',
buildPath: 'src/styles/',
files: [{ destination: 'tokens.css', format: 'css/variables' }]
},
js: {
transformGroup: 'js',
buildPath: 'src/',
files: [{ destination: 'tokens.js', format: 'javascript/es6' }]
}
}
};
Запуск style-dictionary build произведёт CSS custom properties и ES-модули из того же исходного файла, который использует агент.
Использование tokens.json с Tailwind
Небольшой скрипт преобразует tokens.json в расширение темы Tailwind. Структура достаточно плоская, чтобы обходить её без рекурсии для общих случаев:
// scripts/tokens-to-tailwind.js
const fs = require('fs');
const tokens = JSON.parse(fs.readFileSync('design/tokens.json', 'utf8'));
function flattenGroup(group) {
return Object.fromEntries(
Object.entries(group).map(([k, v]) => [k, v.$value])
);
}
const extend = {
colors: flattenGroup(tokens.color),
spacing: flattenGroup(tokens.spacing),
borderRadius: flattenGroup(tokens.radius),
};
console.log(JSON.stringify(extend, null, 2));
node scripts/tokens-to-tailwind.js > design/tailwind-extend.json
Затем в tailwind.config.ts:
import extend from './design/tailwind-extend.json';
export default {
content: ['./src/**/*.{ts,tsx}'],
theme: { extend },
};
Использование tokens.json с Jetpack Compose
Для Android-проектов структура токенов чисто отображается в Kotlin-объект. Его можно генерировать программно или попросить это сделать Claude Code. Для токенов цветов:
// Generated from design/tokens.json
object DesignTokens {
object Color {
val surface = Color(0xFFF6F2EA)
val ink = Color(0xFF1F1D1A)
val accent = Color(0xFFD96A3A)
val accentSoft = Color(0xFFF2C7A8)
}
object Spacing {
val s1 = 4.dp
val s2 = 8.dp
val s4 = 16.dp
val s8 = 32.dp
}
}
Полное руководство с интеграцией MaterialTheme смотрите в статье Jetpack Compose from Figma.
Что не входит в tokens.json
Знание границ области применения помогает сформировать корректные ожидания:
- Тени — figmascope в настоящее время не экспортирует токены drop-shadow и inner-shadow. Значения теней присутствуют непосредственно в узлах IR там, где они есть.
- Градиенты — градиентные заливки экспортируются «как есть» на листовых узлах IR, а не как именованные токены.
- Анимация — тайминги, функции сглаживания и значения переходов выходят за рамки. Поддержка анимации Figma находится в режиме Prototype, который figmascope не читает.
- Контрольные точки — адаптивные ограничения не являются понятием Figma на уровне переменных; они требуют отдельных дизайнерских решений.
Массив warnings в _meta.json укажет на значения, которые не удалось экспортировать чисто. Проверяйте его перед передачей бандла агенту.
Как формируются имена токенов
Когда figmascope читает Figma Variables, имя переменной в Figma становится ключом токена. Переменная с именем Colors/Surface/Primary в коллекции с именем Color становится tokens.color.surface.primary в tokens.json — разделитель пути в Figma /, в JSON — вложенные ключи, разделённые ..
Figma допускает имена переменных с пробелами, заглавными буквами и специальными символами. figmascope нормализует их:
- Пробелы заменяются дефисами:
Primary Surface→primary-surface - Заглавные буквы переводятся в строчные:
AccentOrange→accent-orange - Управляющие символы и Unicode bidi-маркеры удаляются через
sanitizeName - Кириллица и другие не-латинские скрипты транслитерируются для совместимости с slug
Шаг транслитерации важен для интернациональных команд. Украинская дизайн-команда, использующая кириллические имена переменных вроде Фон (фон), получает стабильный ASCII-ключ (fon) в выводе, пригодный для CSS-классов и JSON без проблем с кодировкой. Исходное имя сохраняется в поле $description, если оно присутствует в метаданных Figma Variable.
Проверка покрытия токенами перед генерацией кода
Перед передачей бандла агенту стоит убедиться, что покрытие токенами выглядит разумным. Быстрый Node-скрипт, проверяющий, разрешаются ли все цветовые значения, упомянутые в IR экранов, в записи tokens.json:
// scripts/check-token-coverage.js
const fs = require('fs');
const glob = require('glob');
const tokens = JSON.parse(fs.readFileSync('design/tokens.json', 'utf8'));
const screenFiles = glob.sync('design/screens/*.json');
function getRef(obj) {
const refs = [];
if (obj && typeof obj === 'object') {
if (obj.$ref) refs.push(obj.$ref);
for (const v of Object.values(obj)) refs.push(...getRef(v));
}
return refs;
}
function resolveRef(ref, tokens) {
return ref.split('.').reduce((o, k) => o?.[k], tokens);
}
let missing = 0;
for (const file of screenFiles) {
const screen = JSON.parse(fs.readFileSync(file, 'utf8'));
for (const ref of getRef(screen)) {
if (!resolveRef(ref, tokens)) {
console.error(`Missing token: ${ref} (in ${file})`);
missing++;
}
}
}
if (missing === 0) console.log('All token refs resolve.');
else console.error(`${missing} unresolved token refs.`);
Ноль пропущенных ссылок означает, что агент сможет разрешить каждый $ref в IR разметки без выдумывания значений. Если пропущенные ссылки есть, это обычно означает, что Figma-файл изменился после экспорта бандла — повторно запустите figmascope для свежего экспорта. Дополнительные советы по поддержанию покрытия токенами на протяжении итераций дизайна читайте в блоге figmascope.