Дизайн-токены, темизация и Figma
Дизайн-система живёт не в коде и не в Figma — она живёт между ними. Код — это то, что видит пользователь; Figma — то, что видит дизайнер. Дизайн-токены — мост между этими двумя мирами: именованные значения, которые хранятся в одном месте и генерируются в CSS, JS, iOS, Android и обратно в Figma.
Для UI Engineer это центральная тема. Понимание того, как токены текут из Figma в код, как устроена runtime-темизация через CSS custom properties, чем multi-brand отличается от dark mode и как автоматизировать pipeline через Style Dictionary и Figma MCP, — это то, что отличает Senior от Middle.
Содержание
- Что такое дизайн-токены
- Уровни токенов: primitive, semantic, component
- Типы токенов
- Figma как source of truth
- Figma MCP и Code Connect
- Style Dictionary pipeline
- W3C Design Token Format
- Runtime theming
- Multi-brand и white-label
- Компонентные токены в UI-китах
- Токены и CI/CD
- Лучшие практики и антипаттерны
- Ключевые тезисы для интервью
- Заключение
- Полезные ссылки
Что такое дизайн-токены
Дизайн-токен — именованное значение, которое представляет элемент дизайна: цвет, отступ, радиус, тень, типографику, анимацию. Токены — не CSS-классы и не стили: это атомарные решения, из которых собирается интерфейс.
{
"color": {
"blue": { "500": { "value": "#3b82f6" } }
},
"space": {
"4": { "value": "1rem" }
}
}
Токены отличаются от обычных CSS-переменных источником истины. CSS custom properties — это формат доставки токенов в браузер; сами токены живут в JSON, Figma Variables или Style Dictionary и генерируются в несколько платформ.
Ключевые принципы:
- Single source of truth. Значение определяется один раз, используется везде.
- Платформо-агностичность. Один JSON генерируется в CSS, Swift, Kotlin, JS.
- Семантическое именование. Токены называются по назначению (
color-surface), не по значению (blue-500). - Версионируемость. Токены живут в репозитории, проходят review, релизятся через semver.
Уровни токенов: primitive, semantic, component
Токены организуются в три (иногда четыре) уровня:
Primitive tokens (global tokens)
Сырые значения без семантики. Это палитра и шкала:
{
"color": {
"blue": { "50": { "value": "#eff6ff" } },
"blue": { "500": { "value": "#3b82f6" } },
"blue": { "900": { "value": "#1e3a8a" } }
},
"space": {
"1": { "value": "0.25rem" },
"2": { "value": "0.5rem" },
"4": { "value": "1rem" }
}
}
Примитивы не используются напрямую в компонентах. Это строительный материал.
Semantic tokens (alias tokens)
Токены, привязанные к назначению. Ссылаются на примитивы через aliases:
{
"color": {
"surface": { "value": "{color.white}" },
"surface-alt": { "value": "{color.gray.50}" },
"text-default": { "value": "{color.gray.900}" },
"text-muted": { "value": "{color.gray.500}" },
"primary": { "value": "{color.blue.500}" },
"border": { "value": "{color.gray.200}" }
}
}
Именно семантические токены используются в компонентах. Смена темы — это переназначение семантических токенов на другие примитивы.
Component tokens
Токены для конкретного компонента, когда семантических недостаточно:
{
"button": {
"bg-primary": { "value": "{color.primary}" },
"text-primary": { "value": "{color.text-inverse}" },
"border-radius": { "value": "{radius.md}" },
"padding-x": { "value": "{space.4}" }
}
}
Компонентные токены нужны, когда один семантический токен не покрывает варианты: у кнопки primary и secondary разные фоны, но оба ссылаются на семантический слой.
Иерархия
Primitive (blue-500)
↓ alias
Semantic (color-primary)
↓ alias
Component (button-bg-primary)
Каждый уровень инкапсулирует изменения. Смена blue-500 с #3b82f6 на #2563eb не затрагивает компоненты. Смена color-primary на green-500 меняет все первичные кнопки, но не трогает примитивы.
Типы токенов
| Категория | Примеры | Назначение |
|---|---|---|
| Color | color-surface, color-text-default, color-border, color-primary | Цвета фона, текста, границ, акцентов |
| Spacing | space-xs, space-sm, space-md, space-lg | Отступы, padding, margin, gap |
| Typography | font-family-base, font-size-md, line-height-normal, font-weight-bold | Шрифты, размеры, межстрочные интервалы |
| Border Radius | radius-sm, radius-md, radius-lg, radius-full | Скругления углов |
| Shadow | shadow-sm, shadow-md, shadow-lg | Тени (elevation) |
| Border | border-width-default, border-style-default | Толщина и стиль границ |
| Motion | duration-fast, duration-normal, easing-default | Длительность и easing анимаций |
| Breakpoint | breakpoint-sm, breakpoint-md, breakpoint-lg | Медиа-запросы (не CSS custom properties — генерируются в JS/Tailwind) |
| Z-index | z-index-dropdown, z-index-modal, z-index-toast | Слои наложения |
| Size | size-icon-sm, size-input-height | Размеры иконок, инпутов |
Не все токены становятся CSS custom properties. Breakpoints, например, генерируются в JS-объекты для Tailwind или в SCSS-переменные для @media.
Figma как source of truth
Дизайн-система начинается в Figma. Дизайнер рисует компоненты, используя токены, и эти токены должны синхронизироваться с кодом.
Figma Variables (нативные)
Нативный механизм Figma для хранения токенов:
- Collections — группы переменных (Colors, Spacing, Typography).
- Modes — варианты значений в одной коллекции (Light, Dark, Brand A, Brand B).
- Aliases — переменные могут ссылаться на другие переменные.
- Типы: Color, Number, String, Boolean.
Collection: Colors
Mode: Light
surface = #ffffff
text-default = #111827
Mode: Dark
surface = #111827
text-default = #f9fafb
Переменные используются в компонентах Figma через variable references. При смене mode все компоненты автоматически пересчитываются.
Tokens Studio (Figma Tokens Plugin)
До появления Figma Variables (2023) единственным способом были плагины. Tokens Studio (бывший Figma Tokens Plugin) — самый зрелый:
- Хранит токены в JSON-формате, совместимом со Style Dictionary.
- Экспортирует JSON для pipeline.
- Поддерживает aliases, themes, sets.
- Синхронизируется с Git (через GitHub/GitLab integration).
Когда Figma Variables, когда Tokens Studio:
| Аспект | Figma Variables | Tokens Studio |
|---|---|---|
| Поддержка | Нативная, без плагина | Через плагин |
| Modes | Встроенные | Через sets и themes |
| Экспорт в Style Dictionary | Через REST API | Нативный JSON |
| Legacy | Нет | Да, для старых файлов |
| Сложные трансформации | Нет | Да |
Для новых проектов — Figma Variables. Для legacy или сложной трансформации — Tokens Studio.
Figma MCP и Code Connect
Figma MCP (Model Context Protocol) — сервер, который позволяет AI-агенту читать Figma-файлы и возвращать структурированный контекст. Подробно — в MCP: Model Context Protocol и Design-to-code. Здесь — как MCP встраивается в токен-pipeline.
Code Connect
Code Connect — аннотации в Figma, которые связывают Figma-компонент с кодом в репозитории:
// Button.figma.tsx
import { Button } from './Button';
figma.connect(Button, {
props: {
variant: figma.enum('variant', {
primary: 'primary',
secondary: 'secondary',
}),
size: figma.enum('size', {
sm: 'sm',
md: 'md',
lg: 'lg',
}),
children: figma.string('label'),
},
imports: ['import { Button } from "@ui/button"'],
});
Когда агент вызывает get_design_context по ссылке на Figma-фрейм, MCP-сервер возвращает не просто дерево слоёв, а код с реальными импортами и prop-маппингами:
// Сгенерировано Figma MCP с Code Connect
import { Button } from '@ui/button';
function Screen() {
return <Button variant="primary" size="md">Save</Button>;
}
MCP и токены
Ключ к качеству — что агент видит в контексте. Если в Figma используются токены (через Variables или Tokens Studio), MCP возвращает имена токенов, а не сырые значения:
{
"layer": "Button",
"props": {
"bg": "color-primary",
"text": "color-text-inverse"
}
}
Агент генерирует код с var(--color-primary), а не с #3b82f6. Это и есть разница между «угадай по картинке» и «переиспользуй дизайн-систему».
Практический workflow
- Дизайнер создаёт Figma-компонент, использует Variables для токенов.
- Разработчик настраивает Code Connect для этого компонента.
- В CI Style Dictionary генерирует CSS custom properties из Figma Variables (через Figma REST API).
- Агент получает ссылку на Figma-фрейм, вызывает
get_design_context, и в результате — готовый код с вашими компонентами и токенами.
Style Dictionary pipeline
Style Dictionary (Amazon) — инструмент, который превращает JSON-токены в платформо-специфичные артефакты. Это центр pipeline.
Архитектура pipeline
Figma Variables / Tokens Studio
↓ (REST API / JSON export)
tokens.json
↓
Style Dictionary
↓ ↓ ↓
CSS JS iOS/Android
custom TS Swift/Kotlin
props object
Конфигурация
// style-dictionary.config.js
export default {
source: ['tokens/**/*.json'],
platforms: {
css: {
transformGroup: 'css',
buildPath: 'dist/css/',
files: [{
destination: 'tokens.css',
format: 'css/variables',
}],
},
js: {
transformGroup: 'js',
buildPath: 'dist/js/',
files: [{
destination: 'tokens.js',
format: 'javascript/es6',
}],
},
figma: {
transformGroup: 'figma',
buildPath: 'dist/figma/',
files: [{
destination: 'tokens.json',
format: 'json/flat',
}],
},
},
};
Transforms
Transforms — функции, которые преобразуют значения:
| Transform | Что делает |
|---|---|
name/cti/camel | Имена в camelCase: colorSurface |
name/cti/kebab | Имена в kebab-case: color-surface |
size/pxToRem | 16px → 1rem |
color/css/hex | RGB → hex |
color/hex8android | Hex для Android (с alpha в начале) |
content/icon | Unicode иконки для iOS |
Кастомные transforms
import StyleDictionary from 'style-dictionary';
StyleDictionary.registerTransform({
name: 'size/px',
type: 'value',
matcher: (token) => token.type === 'spacing',
transformer: (token) => `${token.value}px`,
});
Figma Tokens Plugin ↔ Style Dictionary
Tokens Studio экспортирует JSON в формате, совместимом со Style Dictionary:
{
"color": {
"surface": {
"value": "#ffffff",
"type": "color"
},
"primary": {
"value": "{color.blue.500}",
"type": "color"
}
}
}
Style Dictionary разрешает aliases ({color.blue.500} → #3b82f6) и генерирует артефакты.
W3C Design Token Format
Design Tokens Community Group (DTCG) работает над стандартом формата токенов. Текущий черновик — $value, $type, $description:
{
"color": {
"surface": {
"$value": "#ffffff",
"$type": "color",
"$description": "Default surface color"
},
"primary": {
"$value": "{color.blue.500}",
"$type": "color"
}
}
}
Style Dictionary 4+ поддерживает DTCG-формат. Figma Variables могут экспортироваться в DTCG через плагины.
Почему это важно: стандарт позволяет инструментам разных вендоров (Figma, Framer, Storybook) обмениваться токенами без кастомных трансформаций.
Runtime theming
Сгенерированные CSS custom properties нужно переключать в runtime. Приём фреймворк-агностичен: тема — это набор значений на селекторе, а переключение — смена атрибута на <html>.
Паттерн: data-атрибут
/* tokens.css */
:root {
--color-surface: #ffffff;
--color-text-default: #111827;
}
[data-theme="dark"] {
--color-surface: #111827;
--color-text-default: #f9fafb;
}
// vanilla js
const saved = localStorage.getItem('theme') ?? 'light';
function setTheme(next) {
document.documentElement.setAttribute('data-theme', next);
localStorage.setItem('theme', next);
}
setTheme(saved);
Почему атрибут, а не класс:
- Не влияет на специфичность селекторов.
- Легче комбинировать (theme + brand + reduced-motion).
- Семантически яснее.
Интеграция с фреймворками
Сам pipeline не зависит от фреймворка — меняется только обёртка вокруг setAttribute. Все варианты делают одно: читают сохранённое значение, ставят атрибут на <html> и слушают системную тему через matchMedia('(prefers-color-scheme: dark)').
| Фреймворк | Обёртка |
|---|---|
| Vue | composable useTheme + watch |
| React | контекст (ThemeProvider) или useSyncExternalStore |
| Svelte | store + $effect |
| Angular | сервис + DOCUMENT / Renderer2 |
Типовая реализация обёртки (на примере Vue):
// composables/useTheme.ts
import { ref, watch } from 'vue';
export type Theme = 'light' | 'dark' | 'system';
export function useTheme() {
const theme = ref<Theme>(
(localStorage.getItem('theme') as Theme) || 'system'
);
const resolved = ref<'light' | 'dark'>(
theme.value === 'system'
? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
: theme.value
);
watch([theme, resolved], ([t, r]) => {
document.documentElement.setAttribute('data-theme', r);
localStorage.setItem('theme', t);
}, { immediate: true });
function setTheme(t: Theme) {
theme.value = t;
resolved.value = t === 'system'
? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
: t;
}
return { theme, resolved, setTheme };
}
Динамические значения из компонентов
Токен можно переопределить на уровне компонента, прокинув значение в inline custom property — это работает в любом фреймворке:
<button class="btn" style="--button-accent: #7c3aed">Click me</button>
.btn {
background: var(--button-accent);
}
Фреймворки дают лишь удобный способ привязать пропс к переменной: Vue — useCssVars / v-bind() в CSS, React — style={{ '--button-accent': color }}, Angular — [style.--button-accent].
Multi-brand и white-label
Dark mode — переназначение semantic-токенов на те же primitive. Multi-brand — отдельный набор semantic-токенов на каждый бренд.
Архитектура multi-brand
// tokens/brand-a/semantic.json
{
"color": {
"primary": { "value": "{color.blue.500}" },
"surface": { "value": "{color.white}" }
}
}
// tokens/brand-b/semantic.json
{
"color": {
"primary": { "value": "{color.green.500}" },
"surface": { "value": "{color.gray.50}" }
}
}
Style Dictionary генерирует отдельный CSS-файл на бренд:
/* tokens-brand-a.css */
[data-brand="a"] {
--color-primary: #3b82f6;
--color-surface: #ffffff;
}
/* tokens-brand-b.css */
[data-brand="b"] {
--color-primary: #22c55e;
--color-surface: #f9fafb;
}
Runtime-переключение
function setBrand(brand: 'a' | 'b') {
document.documentElement.setAttribute('data-brand', brand);
}
Figma modes для brands
В Figma Variables это modes:
Collection: Colors
Mode: Brand A
primary = {blue.500}
Mode: Brand B
primary = {green.500}
Дизайнер переключает mode в Figma и видит оба бренда. Style Dictionary экспортирует каждый mode в отдельный файл.
Компонентные токены в UI-китах
Популярные UI-киты строятся на токенах и отдают их наружу — как CSS custom properties или как JS-объект темы. Принцип одинаков в любом фреймворке.
CSS custom properties
Одни киты (Element Plus, Ant Design v5, Shoelace) экспонируют переменные с префиксом, которые переопределяются в вашем CSS:
:root {
--el-color-primary: #3b82f6; /* Element Plus */
--ant-color-primary: #3b82f6; /* Ant Design v5 */
}
JS-объект темы
Другие (Vuetify, Material UI, Chakra) принимают тему как JS/TS-объект и генерируют переменные в runtime:
createTheme({
palette: {
primary: { main: '#3b82f6' },
},
});
Runtime-генерация даёт гибкость, но добавляет overhead по сравнению со статически сгенерированным CSS.
Unstyled / headless-режим
Отдельный подход — кит не навязывает стили, а даёт хуки для вашей дизайн-системы (PrimeVue unstyled, Headless UI, Radix):
<Button class="px-4 py-2 rounded bg-blue-600 text-white" />
Компонентные токены в своём UI-ките
Если вы строите свой UI-кит, паттерн:
<button class="btn btn--primary btn--md">
Click me
</button>
.btn {
background: var(--button-bg);
color: var(--button-text);
padding: var(--button-padding);
border-radius: var(--button-border-radius);
}
.btn--primary {
--button-bg: var(--color-primary);
--button-text: var(--color-text-inverse);
}
.btn--secondary {
--button-bg: var(--color-surface-alt);
--button-text: var(--color-text-default);
}
.btn--sm {
--button-padding: var(--space-2) var(--space-3);
}
.btn--md {
--button-padding: var(--space-3) var(--space-4);
}
.btn--lg {
--button-padding: var(--space-4) var(--space-5);
}
Компонентные токены (--button-bg) ссылаются на семантические (--color-primary), которые ссылаются на primitive (--blue-500). Это даёт три уровня кастомизации:
- Primitive: сменить
blue-500→ все синие элементы меняются. - Semantic: сменить
color-primaryнаgreen-500→ все primary-элементы меняются. - Component: сменить
--button-bg→ только кнопка меняется.
Токены и CI/CD
Токены — код. Они должны проходить через CI/CD, как любой другой код.
Pipeline
Figma (дизайнер обновляет Variables)
↓
Figma REST API / Tokens Studio export
↓
tokens.json в репозитории
↓
CI: Style Dictionary build
↓
CI: lint (именование, типы, отсутствующие aliases)
↓
CI: snapshot-тесты (CSS/JS артефакты не изменились)
↓
CI: visual regression (Chromatic/Percy/Loki)
↓
npm publish / deploy
Lint-правила
// scripts/lint-tokens.js
import tokens from '../tokens.json' assert { type: 'json' };
function lintTokens(obj, path = '') {
for (const [key, value] of Object.entries(obj)) {
if (value.value) {
// Проверка: значение — не сырой hex, а alias
if (/^#[0-9a-f]{6}$/i.test(value.value) && path.includes('component')) {
throw new Error(
`Component token ${path}.${key} uses raw value ${value.value}. Use alias instead.`
);
}
}
if (typeof value === 'object' && !value.value) {
lintTokens(value, `${path}.${key}`);
}
}
}
lintTokens(tokens);
Snapshot-тесты
// tests/tokens.snapshot.test.ts
import { describe, it, expect } from 'vitest';
import tokens from '../dist/js/tokens.js';
describe('design tokens', () => {
it('matches snapshot', () => {
expect(tokens).toMatchSnapshot();
});
});
Если дизайнер меняет blue-500 с #3b82f6 на #2563eb, snapshot падает. Это сигнал, что токены изменились, и нужно проверить visual regression.
Visual regression
Инструменты Chromatic, Percy, Loki сравнивают скриншоты компонентов до и после изменения токенов:
// chromatic test
import { composeStories } from '@storybook/react';
import * as stories from './Button.stories';
const { Primary, Secondary } = composeStories(stories);
test('Primary button should not change visually', async () => {
const image = await Primary.compose().snapshot();
expect(image).toMatchImageSnapshot();
});
Если изменение токенов намеренное — апрувните снимок в Chromatic. Если нет — откатите.
Лучшие практики и антипаттерны
Делайте
- Используйте Figma Variables для новых проектов — нативная поддержка modes и aliases.
- Настройте Code Connect для ключевых компонентов — без него Figma MCP угадывает.
- Храните токены в репозитории, а не только в Figma — версионируемость и review.
- Разделяйте уровни токенов: primitive, semantic, component. Не пропускайте semantic.
- Используйте aliases в semantic и component — это даёт гибкость при смене темы.
- Автоматизируйте pipeline через Style Dictionary и CI — ручная генерация устаревает.
- Тестируйте токены через snapshot-тесты и visual regression.
- Документируйте токены в Storybook или docs-сайте с живыми примерами.
Не делайте
- Не используйте primitive-токены в компонентах (
color-blue-500вместоcolor-primary). - Не хардкодьте значения в компонентах (
background: #3b82f6— антипаттерн). - Не создавайте компонентные токены без необходимости — если хватает semantic, не плодите сущности.
- Не генерируйте токены вручную — автоматизируйте через Style Dictionary.
- Не игнорируйте visual regression — изменение токена может сломать контраст или spacing.
- Не смешивайте runtime theming с изолированными стилями — scoped/CSS Modules не комбинируются с глобальными custom properties предсказуемо.
- Не храните токены только в Figma — без экспорта в репозиторий нет версионируемости.
Ключевые тезисы для интервью
- Дизайн-токены — именованные значения дизайна, организованные в три уровня: primitive (сырые значения), semantic (назначения со ссылками на primitive через aliases) и component (привязка к конкретному компоненту). Семантический слой позволяет менять тему переназначением aliases, не трогая примитивы.
- Figma Variables — нативный механизм для хранения токенов с modes (Light, Dark, Brand A) и aliases; Tokens Studio — внешний плагин с JSON-экспортом для Style Dictionary. Для новых проектов предпочтительнее Variables.
- Figma MCP через
get_design_contextвозвращает структурированное дерево слоёв с подстановкой реальных импортов и prop-маппингов из Code Connect — без Code Connect агент угадывает компонент по картинке, с ним переиспользует существующий. - Style Dictionary принимает JSON-токенов и генерирует платформо-специфичные артефакты (CSS custom properties, JS/TS, Swift, Kotlin) через transforms (camelCase, kebab-case, px→rem) и форматы; конфигурация описывает source, platforms и transforms.
- Runtime theming — переключение набора CSS custom properties через data-атрибут (
data-theme="dark") на<html>; атрибут предпочтительнее класса, так как не влияет на специфичность и комбинируется сdata-brand. Фреймворки лишь оборачиваютsetAttributeв composable/hook/provider. - Multi-brand требует отдельного набора semantic-токенов на каждый бренд (отдельные JSON-файлы → отдельные CSS-файлы → runtime-переключение через
data-brand); dark mode — только переназначение semantic-токенов на те же primitive. - CI/CD для токенов: Style Dictionary в build-пайплайне, lint (именование, отсутствие сырых значений в компонентах), snapshot-тесты артефактов, visual regression через Chromatic/Percy/Loki — изменение токена триггерит проверку контраста и spacing.
Заключение
Дизайн-токены — это мост между Figma и кодом. Они организуются в три уровня (primitive → semantic → component), хранятся в JSON или Figma Variables, генерируются через Style Dictionary в CSS, JS и другие платформы и поставляются в приложение как CSS custom properties. Runtime theming переключает наборы токенов через data-атрибуты, multi-brand требует отдельного набора semantic-токенов на каждый бренд.
Figma MCP и Code Connect превращают design-to-code из угадывания в переиспользование компонентов, но только при условии, что токены настроены. CI/CD для токенов включает lint, snapshot-тесты и visual regression — это защита от незаметных изменений контраста и spacing.
Для UI Engineer важно понимать не просто «что такое токены», а как они текут из Figma в код, как устроена runtime-темизация на CSS custom properties, чем multi-brand отличается от dark mode, и как автоматизировать pipeline. Это то, что отличает Senior от Middle на собеседовании.
Полезные ссылки
- Style Dictionary — официальная документация.
- Design Tokens W3C Community Group — спецификация формата токенов.
- Figma Variables — руководство по Variables.
- Figma MCP Server — документация MCP-сервера.
- Figma Code Connect — интеграция кода с Figma.
- Tokens Studio — плагин для работы с токенами в Figma.
- Chromatic — visual regression testing.
- Design Tokens Community Group — W3C инициатива.
- Figma REST API — для автоматизации экспорта Variables.