Дизайн-токени — це спільна мова між дизайнерами та розробниками. Вони існують у різних формах ще з епохи 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."
}
]
}
Виведені токени корисні, але не авторитетні. Ставтесь до них як до відправної точки, а не специфікації дизайн-системи.
Посилання на токени в layout 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 module exports з того ж вихідного файлу, що використовує агент.
Використання 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 object. Можна згенерувати його програмно або попросити 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 - Кирилиця та інші не-латинські скрипти транслітеруються для сумісності зі слагами
Крок транслітерації важливий для міжнародних команд. Українська дизайнерська команда, що використовує кириличні назви змінних на кшталт Фон (background), отримає стабільний ASCII-ключ (fon) у результаті, придатний для CSS class-назв і 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 у layout IR без вигадування значень. Якщо є відсутні посилання — зазвичай це означає, що Figma-файл змінився після того, як бандл був експортований — повторно запустіть figmascope для свіжого експорту. На блозі figmascope є більше порад щодо підтримання покриття токенів між ітераціями дизайну.