Уроки

Дизайн-токены для цвета: от Figma к коду

8 мин чтения

Дизайн-токены — это словарь дизайн-системы. Они заменяют скопированные вручную значения вроде #1D4ED8, рассыпанные по десяткам файлов, именованными ссылками вроде color.primary.600, несущими смысл, отслеживаемыми до источника и трансформируемыми в любой формат, необходимый каждой платформе. Цветовые токены — наиболее важная категория: они управляют каждой поверхностью, каждым текстовым элементом, каждым интерактивным состоянием. Правильно их настроить — значит отличить дизайн-систему, которая масштабируется, от той, которая рассыпается под весом собственной несогласованности. Это руководство охватывает полный пайплайн: соглашения об именовании, экспорт из Figma, трансформация в Style Dictionary и мультиплатформенный вывод.

Что такое дизайн-токены

Дизайн-токен — наименьшее неделимое решение в дизайн-системе, выраженное в виде пары имя-значение. Для цвета токен сопоставляет осмысленное имя со значением цвета:

{
  "color": {
    "blue": {
      "500": { "value": "#3B82F6", "type": "color" }
    },
    "primary": {
      "default": { "value": "{color.blue.500}", "type": "color" }
    }
  }
}

В этом примере существует два типа токенов:

  • Примитивные токены (color.blue.500): сырые значения. Они описывают, чем что-то является.
  • Семантические токены (color.primary.default): ссылки на примитивы. Они описывают, что что-то делает.

Эта двухуровневая структура лежит в основе любой зрелой цветовой системы. Примитивы дают полную палитру. Семантика обеспечивает возможность ребрендинга — измените то, на что указывает color.primary.default, и каждый компонент на каждой платформе, использующей его, обновится автоматически.

Токены vs. переменные

CSS-переменные — не токены. Это один из возможных форматов вывода для токенов. Токен существует в платформо-нейтральном JSON-файле; CSS-переменная — это то, чем токен становится после трансформации для веба. На iOS тот же токен становится Swift-константой цвета; на Android — XML-ресурсом цвета. Токен — источник истины. Переменные, константы и XML-значения — все они производные.

Соглашения об именовании токенов для цвета

Именование — место, где большинство команд либо создают устойчивую систему, либо загоняют себя в угол. Хорошее именование цветовых токенов следует трём принципам: иерархия, роль и специфичность.

Трёхуровневая иерархия

Уровень Пример Назначение
Примитивный color.blue.500 Сырая палитра — все цвета, которые знает система
Семантический color.action.primary Что делает цвет — независимо от оттенка
Компонентный color.button.primary.background Где конкретно появляется цвет

Команды часто останавливаются на двух уровнях. Компонентные токены ценны для систем с десятками компонентов, поддерживаемых несколькими командами: они позволяют команде кнопок изменить color.button.primary.background, не затрагивая всех остальных потребителей color.action.primary. Для меньших систем достаточно двух уровней.

Паттерны именования

color.{palette}.{step}          # Примитив: color.blue.500
color.{role}.{variant}          # Семантика: color.text.secondary
color.{role}.{state}            # Состояние: color.action.primary.hover
color.{component}.{element}     # Компонент: color.navbar.background

Избегайте имён, кодирующих визуальное значение: - color.blue-button — ломается, когда кнопка становится фиолетовой - color.dark-background — ломается в тёмной теме, где «dark» теряет смысл - color.light-gray-border — как только вы меняете оттенок, имя становится неверным

Предпочитайте имена, кодирующие назначение: - color.action.default — выдерживает ребрендинги - color.surface.raised — выдерживает смену тем - color.border.subtle — выдерживает подстройку оттенков

Именование шкалы для примитивов

Для цветовой шкалы большинство команд принимают одно из двух соглашений:

Числовое (в стиле Tailwind): color.blue.50, color.blue.100color.blue.950. Интуитивно для разработчиков, напрямую соответствует классам Tailwind.

Описательное (в стиле Material): color.blue.lightest, color.blue.light, color.blue.default, color.blue.dark, color.blue.darkest. Более читаемо в дизайн-инструментах, сложнее расширять (что светлее lightest?).

Числовое соглашение более поддерживаемо в масштабе. Используйте Генератор оттенков для создания полной шкалы 50–950 из любого стартового цвета бренда — вывод напрямую соответствует числовым именам токенов.

Рабочий процесс с плагином Figma Tokens

Figma Variables (представленные в 2023 году) и плагин Tokens Studio for Figma (ранее Figma Tokens) оба обеспечивают управление токенами внутри Figma. Они существенно различаются по возможностям:

Функция Figma Variables Tokens Studio
Типы токенов Цвет, Число, Строка, Булево 24+ типов, включая типографику, тени
Экспорт JSON Ограниченный (через REST API) Напрямую в JSON-файл
Псевдонимы Да Да
Мультирежим Да (до 4 режимов в бесплатном уровне) Да
Совместимость со Style Dictionary С плагином Нативно

Настройка Variables в Figma

  1. Откройте файл Figma и перейдите к Local Variables (правая панель или меню Edit).
  2. Создайте коллекцию с именем Primitives с одним режимом.
  3. Добавьте переменные цвета по соглашению об именовании color/blue/500, color/blue/600 и т. д. Figma использует прямые косые черты как разделители иерархии, которые большинство инструментов экспорта преобразуют в вложенные JSON-объекты.
  4. Создайте вторую коллекцию с именем Semantics с двумя режимами: Light и Dark.
  5. Добавьте семантические переменные (color/action/primary, color/surface/background) и установите их значения как псевдонимы, указывающие на переменные Primitives. Ключевая возможность: в режиме Light color/surface/background указывает на color/neutral/50, а в режиме Dark — на color/neutral/900.

Это псевдонимирование на основе режимов обеспечивает единый семантический токен, разрешающийся в разные значения в зависимости от активной темы, — без дублирования определений компонентов.

Экспорт с Tokens Studio

Tokens Studio может записывать файлы токенов напрямую в репозиторий GitHub при каждом сохранении, поддерживая живую синхронизацию между Figma и кодовой базой:

  1. Установите плагин Tokens Studio for Figma.
  2. В настройках плагина подключитесь к репозиторию GitHub в разделе Sync → GitHub.
  3. Укажите путь к файлу вывода (например, tokens/tokens.json).
  4. Включите Auto-push on save для синхронизации репозитория при каждом изменении.

Экспортируемый JSON следует формату W3C Design Token Community Group или проприетарному формату Tokens Studio — оба воспринимаются Style Dictionary.

Конфигурация Style Dictionary

Style Dictionary — стандартный инструмент для трансформации JSON дизайн-токенов в платформо-специфичные выводы. Вы определяете правила трансформации в файле config.json (или config.js), задающем, как следует преобразовывать значения токенов и куда записывать файлы вывода.

Установка

npm install --save-dev style-dictionary
# или
npm install -g style-dictionary

Базовая конфигурация

// style-dictionary.config.js
module.exports = {
  source: ['tokens/**/*.json'],
  platforms: {
    css: {
      transformGroup: 'css',
      buildPath: 'src/styles/',
      files: [
        {
          destination: 'tokens.css',
          format: 'css/variables',
          selector: ':root',
        },
      ],
    },
  },
}

Запустите сборку:

npx style-dictionary build --config style-dictionary.config.js

Пользовательские трансформации

Встроенная группа трансформаций css обрабатывает большинство случаев, однако цветовые токены нередко требуют пользовательских трансформаций — например, конвертации hex-значений в OKLCH или удаления прозрачности из токенов, которые всегда должны быть непрозрачными.

// style-dictionary.config.js
const StyleDictionary = require('style-dictionary')

// Пользовательская трансформация: hex → каналы HSL (для совместимости с shadcn/ui)
StyleDictionary.registerTransform({
  name: 'color/hsl-channels',
  type: 'value',
  matcher: (token) => token.attributes.category === 'color',
  transformer: (token) => {
    const hex = token.value
    const { h, s, l } = hexToHsl(hex) // реализуйте hexToHsl отдельно
    return `${h.toFixed(1)} ${s.toFixed(1)}% ${l.toFixed(1)}%`
  },
})

function hexToHsl(hex) {
  // Стандартная конвертация hex → HSL
  let r = parseInt(hex.slice(1, 3), 16) / 255
  let g = parseInt(hex.slice(3, 5), 16) / 255
  let b = parseInt(hex.slice(5, 7), 16) / 255
  const max = Math.max(r, g, b), min = Math.min(r, g, b)
  let h, s, l = (max + min) / 2
  if (max === min) {
    h = s = 0
  } else {
    const d = max - min
    s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
    switch (max) {
      case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break
      case g: h = ((b - r) / d + 2) / 6; break
      case b: h = ((r - g) / d + 4) / 6; break
    }
  }
  return { h: h * 360, s: s * 100, l: l * 100 }
}

module.exports = {
  source: ['tokens/**/*.json'],
  platforms: {
    css: {
      transforms: ['attribute/cti', 'name/cti/kebab', 'color/hsl-channels'],
      buildPath: 'src/styles/',
      files: [
        {
          destination: 'tokens.css',
          format: 'css/variables',
          selector: ':root',
        },
      ],
    },
  },
}

Это даёт вывод, совместимый с соглашением HSL-каналов shadcn/ui:

:root {
  --color-blue-500: 217.2 91.2% 59.8%;
  --color-action-primary: 217.2 91.2% 59.8%;
}

Обработка псевдонимов

Style Dictionary разрешает псевдонимы токенов автоматически. Токен вроде:

{
  "color": {
    "action": {
      "primary": { "value": "{color.blue.500}" }
    }
  }
}

Создаёт --color-action-primary с разрешённым значением color.blue.500, а не строкой псевдонима. Для мультирежимных (светлых/тёмных) токенов используйте отдельные JSON-файлы на режим и ссылайтесь на них в массиве source:

// Отдельные сборки для светлого и тёмного
module.exports = {
  source: ['tokens/primitives.json', 'tokens/semantics-light.json'],
  platforms: {
    'css/light': {
      buildPath: 'src/styles/',
      files: [{ destination: 'tokens-light.css', format: 'css/variables', selector: ':root' }],
    },
  },
}

Мультиплатформенный вывод: CSS, iOS и Android

CSS-вывод

CSS-вывод рассмотрен выше. Полный файл токенов CSS для типичной дизайн-системы может выглядеть так:

:root {
  /* Примитивы */
  --color-blue-50:  #EFF6FF;
  --color-blue-500: #3B82F6;
  --color-blue-900: #1E3A8A;

  /* Семантика */
  --color-action-primary:       #3B82F6;
  --color-action-primary-hover: #2563EB;
  --color-surface-background:   #FFFFFF;
  --color-text-primary:         #111827;
  --color-text-secondary:       #6B7280;
}

Используйте Генератор палитр для разработки примитивной палитры перед её кодификацией в JSON. Генератор позволяет исследовать дополнительные, аналогичные и триадные палитры из стартового цвета бренда.

iOS (Swift)-вывод

Встроенная группа трансформаций ios-swift Style Dictionary генерирует Swift-файл со статическими расширениями цвета:

// style-dictionary.config.js
platforms: {
  ios: {
    transformGroup: 'ios-swift',
    buildPath: 'ios/ColorTokens/',
    files: [
      {
        destination: 'ColorTokens.swift',
        format: 'ios-swift/class.swift',
        className: 'ColorTokens',
      },
    ],
  },
}

Вывод:

// ColorTokens.swift (автогенерация — не редактировать)
import UIKit

public class ColorTokens: NSObject {
    public static let colorBlue500 = UIColor(
        red: 59.0/255.0, green: 130.0/255.0, blue: 246.0/255.0, alpha: 1.0
    )
    public static let colorActionPrimary = UIColor(
        red: 59.0/255.0, green: 130.0/255.0, blue: 246.0/255.0, alpha: 1.0
    )
}

Для SwiftUI пользовательский формат генерирует Color вместо UIColor:

StyleDictionary.registerFormat({
  name: 'ios/swiftui',
  formatter: ({ dictionary }) => {
    const tokens = dictionary.allTokens
      .filter(t => t.attributes.category === 'color')
      .map(t => {
        const name = t.name.replace(/-./g, m => m[1].toUpperCase())
        const hex = t.value
        const r = parseInt(hex.slice(1, 3), 16) / 255
        const g = parseInt(hex.slice(3, 5), 16) / 255
        const b = parseInt(hex.slice(5, 7), 16) / 255
        return `  static let ${name} = Color(red: ${r.toFixed(4)}, green: ${g.toFixed(4)}, blue: ${b.toFixed(4)})`
      }).join('\n')
    return `import SwiftUI\n\nextension Color {\n${tokens}\n}`
  },
})

Android (XML)-вывод

Для Android стандартный вывод — файл ресурсов colors.xml:

platforms: {
  android: {
    transformGroup: 'android',
    buildPath: 'android/app/src/main/res/values/',
    files: [
      {
        destination: 'colors.xml',
        format: 'android/colors',
      },
    ],
  },
}

Вывод:

<?xml version="1.0" encoding="UTF-8"?>
<resources>
  <color name="color_blue_500">#FF3B82F6</color>
  <color name="color_action_primary">#FF3B82F6</color>
</resources>

Обратите внимание: формат цвета Android — ARGB (#AARRGGBB), а не RGB. Группа трансформаций Android в Style Dictionary обрабатывает это преобразование автоматически.

Вывод для Compose (Kotlin)

Для Jetpack Compose пользовательский формат генерирует Kotlin-константы цветов:

StyleDictionary.registerFormat({
  name: 'compose/colors',
  formatter: ({ dictionary }) => {
    const tokens = dictionary.allTokens
      .filter(t => t.attributes.category === 'color')
      .map(t => {
        const hex = t.value.replace('#', '')
        const name = t.name.replace(/-./g, m => m[1].toUpperCase())
        return `  val ${name} = Color(0xFF${hex.toUpperCase()})`
      }).join('\n')
    return `package com.example.tokens\n\nimport androidx.compose.ui.graphics.Color\n\nobject ColorTokens {\n${tokens}\n}`
  },
})

Интеграция токенов в CI/CD

Дизайн-токены становятся наиболее мощными, когда пайплайн от Figma до кода автоматизирован. Типичная интеграция CI/CD:

  1. Figma → GitHub: Tokens Studio отправляет tokens.json в ветку при каждом сохранении дизайнером.
  2. GitHub Action: рабочий процесс запускает Style Dictionary при каждом пуше в ветку токенов и коммитит сгенерированные файлы (tokens.css, ColorTokens.swift, colors.xml) в основную ветку.
  3. Репозитории платформ: веб-, iOS- и Android-репозитории подтягивают из общего репозитория токенов через npm-пакет или git-подмодуль.
# .github/workflows/build-tokens.yml
name: Build Design Tokens

on:
  push:
    paths:
      - 'tokens/**'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npx style-dictionary build --config style-dictionary.config.js
      - uses: stefanzweifel/git-auto-commit-action@v5
        with:
          commit_message: 'chore: rebuild design tokens'
          file_pattern: 'src/styles/ ios/ android/'

Эта автоматизация устраняет ручное копирование, являющееся причиной большинства расхождений токенов.

Ключевые выводы

  • Дизайн-токены отделяют «что» (примитивные токены: color.blue.500) от «зачем» (семантические токены: color.action.primary). Эта двухуровневая структура позволяет проводить ребрендинг, изменив один псевдоним, а не разыскивая hex-коды по всей кодовой базе.
  • Имена токенов должны кодировать назначение, а не внешний вид. Имя вроде color.surface.raised выдерживает любую смену оттенков и ребрендинг; color.light-gray-background — нет.
  • Figma Variables с поддержкой мультирежима обеспечивают осведомлённое о теме псевдонимирование токенов прямо в дизайн-инструменте. Tokens Studio для Figma расширяет это прямой синхронизацией с GitHub и более богатыми типами токенов.
  • Style Dictionary трансформирует JSON токенов в платформо-специфичные выводы — CSS-переменные для веба, UIColor/SwiftUI Color для iOS, XML-ресурсы для Android — из единственного источника истины.
  • Автоматизируйте пайплайн Figma → код с помощью GitHub Actions, запускающего Style Dictionary при каждом изменении токенов и коммитящего сгенерированные файлы, устраняя ручную синхронизацию.
  • Используйте Генератор палитр для изучения примитивной цветовой палитры перед кодификацией токенов и Генератор оттенков для создания полных числовых шкал, готовых к именованию токенов.

Похожие цвета

Похожие бренды

Похожие инструменты