Перейти к содержанию

FISYCO

Публичная бета

Режимы токенов и тёмная тема без дублирования

6 мин чтения

Инженерия
One card of token names feeding a light panel and a dark panel, with the same names on both

Тёмная тема часто начинается с копии. Кто-то дублирует файл с цветами, называет его dark.css и правит значения. Это работает до следующей правки: новый токен попадает в один файл и не попадает в другой, и тёмная тема тихо отстаёт. Режимы токенов решают это так: набор имён остаётся один, а переключаются только значения.

Здесь мы показываем, как собрать тёмную тему на дизайн-токенах без дублирования: как режимы устроены в Figma, что о темах теперь говорит формат DTCG и какими тремя способами выпустить режимы в CSS.

Коротко

  • Тема означает второй набор значений для тех же имён, а не второй набор токенов. Компоненты читают color-bg-surface при любой теме.
  • Между светлой и тёмной темой меняется только смысловой уровень. Примитивы остаются одной палитрой, а код компонентов не знает, какой режим включён.
  • Режимы переменных Figma и Resolver Module из DTCG описывают это одинаково: один токен, по одному значению на каждый контекст.
  • В CSS режимы выпускают блоками с атрибутом, отдельными файлами или через light-dark(). Каждый способ подходит своей схеме.

Один набор имён, два набора значений

Режим задаёт контекст, в котором один и тот же токен получает другое значение. Обычная пара: светлый и тёмный; бренды, плотность и платформы устроены так же. Условный набор смысловых токенов:

Имена в обеих колонках одинаковые. Меняются только ссылки, и ведут они в одну общую палитру. Вот что на практике значит «без дублирования»: нет никакого dark-color-bg-surface и нет компонента, которому приходится выбирать между двумя именами.

Это работает, только если токены разделены на уровни. Примитивные токены вроде gray.900 хранят сырые значения и от режима не зависят. Смысловые токены вроде color-bg-surface называют назначение, и переключаются именно они. Подробно об уровнях написано в нашем руководстве по именам дизайн-токенов.

Крупные публичные системы описывают то же разделение. GitHub Primer пишет, что «благодаря инвертированным шкалам светлая и тёмная темы могут использовать многие из одних и тех же функциональных цветовых токенов без специальных переопределений» (Primer). Atlassian говорит короче: «Темы позволяют нам переключать цветовые схемы и стили везде с помощью единого набора токенов» (Atlassian).

Режимы в Figma

В Figma режимы встроены в переменные. Справочный центр пишет: «Режимы переменных позволяют представить разные контексты макетов, не создавая отдельные фреймы для каждого контекста» (Figma).

У коллекции по одной колонке на режим, и у каждой переменной есть значение в каждой колонке. Фрейм выбирает режим через Apply variable mode. Если там стоит Auto, фрейм наследует режим родителя или берёт режим коллекции по умолчанию.

Две привычки упрощают экспорт режимов из Figma:

  • Режимы на смысловой коллекции. Держите примитивы в коллекции с одним режимом, а смысловые переменные пусть ссылаются на них в каждом режиме. Тогда в экспорт уходят ссылки, а не скопированные hex-значения.
  • Имена режимов для кода. «Light» и «Dark» потом станут именами файлов и селекторами. «Mode 1» обычно превращается во что-то вроде mode-1, и разработчику это ничего не говорит. Мы узнали это на собственном экспортёре: когда-то он называл файлы режимов по внутреннему ID режима в Figma, который выглядит как 1:0. Получались файлы вроде tokens.1:0.css, а двоеточие в имени файла Windows не допускает. Теперь файлы называются по имени режима.

Режимы в формате DTCG

Формат DTCG дошёл до релиза 2025.10, а темы живут в отдельном Resolver Module, опубликованном в тот же день. Документ резолвера перечисляет наборы (sets) файлов токенов и модификаторы (modifiers). Модификатор обеспечивает «условное включение через карту контекстов», а resolutionOrder задаёт, какие наборы и модификаторы применяются и в каком порядке (DTCG Resolver Module).

Модификатор темы выглядит так (пример упрощён по образцу из самой спецификации):

Файлы, на которые указывает каждый контекст, содержат те же имена токенов с другими значениями. Это та же модель, что и у коллекции в Figma, только записанная как стандарт.

Три способа выпустить режимы в CSS

Когда значения есть, остаётся вопрос, как браузер переключается между ними. Распространённых вариантов три.

1. Блоки с атрибутом в одном файле

Все темы живут в одной таблице стилей. Значения по умолчанию лежат в :root, а каждый режим переопределяет их под селектором атрибута:

Чтобы сменить тему, достаточно поставить data-theme="dark" на <html> или на любое поддерево. Поэтому это самый гибкий вариант: тёмная боковая панель внутри светлой страницы стоит одного атрибута. Чтобы следовать настройке операционной системы, выставляйте атрибут по prefers-color-scheme небольшим скриптом или продублируйте блок внутри этого медиазапроса.

2. Файл на каждый режим

У каждого режима своя таблица стилей, например tokens.light.css и tokens.dark.css. Вы загружаете одну и подменяете её или подключаете обе за медиазапросами:

Такой вариант подходит приложениям с множеством режимов, например с несколькими брендами, где отдавать каждый бренд каждому пользователю было бы расточительно. Если файлы генерируются, они не могут разойтись так, как расходится скопированный вручную dark.css.

3. light-dark()

Самый новый вариант держит оба значения в одном объявлении. MDN пишет, что light-dark() «принимает два цвета или два изображения и возвращает цвет или изображение в зависимости от активной цветовой схемы, без медиафункции prefers-color-scheme». Нужен color-scheme: light dark на элементе или на корне. Для цветов функция входит в Baseline с мая 2024 года. Аргументы-изображения появились позже, так что проверьте поддержку, прежде чем на них опираться (MDN).

Запись компактная, а переключение берёт на себя браузер. Но она покрывает только две схемы и только цвета и изображения. Для брендов или режимов плотности всё равно нужен один из первых двух вариантов.

Как не дать режимам разойтись

Сгенерированные режимы не разъедутся из-за копирования, но всё ещё могут отойти от макета.

  • Проверяйте полноту. У каждого смыслового токена должно быть значение в каждом режиме. В схеме с блоками атрибутов пропущенное тёмное значение откатится к значению из :root и ночью обернётся ярко-светлой панелью.
  • Не пускайте литералы в режимы. Значение режима должно ссылаться на примитив. Hex-значение, вписанное в тёмную колонку, обходит палитру: это такой же обход системы, как hex в компоненте.
  • Выпускайте изменения режимов через pull request. Сопоставляйте переменные по их ID в Figma, а не по именам: тогда переименование будет выглядеть как переименование, а не как удаление плюс добавление. Когда значение режима меняется в Figma, код должен получить ту же правку в pull request, а не при следующем ручном экспорте. Сборку разбирает статья Style Dictionary на практике.

Где здесь FISYCO

Мы делаем FISYCO, чтобы режимы из переменных Figma приходили в код. Библиотека токенов выгружает каждый режим в выбранном вами формате. Со встроенными режимами в tokens.css лежат значения по умолчанию в :root и по одному блоку [data-theme="…"] на режим, названному по режиму Figma. С отдельными файлами у каждого режима свой tokens.<mode>.css, а при экспорте SCSS ещё и tokens.<mode>.scss. Экспорт DTCG пишет по одному design-tokens.<mode>.tokens.json на режим, а документ резолвера пока не пишет.

Алиасы остаются алиасами, поэтому тёмное значение, которое ссылается на gray.900, приходит как var(--…), а не как скопированный hex. Изменения приходят в pull request, когда вы публикуете библиотеку в Figma, или по запросу.

Выгрузите режимы Figma в токены и держите светлую и тёмную тему в одном наборе имён.
#Дизайн-токены#Figma
KK

Автор статьи

Kirill Karpov

Основатель FISYCO

Кирилл Карпов — основатель FISYCO, инструмента контроля дизайн-системы, который держит Figma и код продакшена в согласии.

Новые статьи на почту

Дизайн-системы, токены и доставка дизайна в код. Без спама, отписка в один клик.

Подписываясь, вы соглашаетесь получать письма блога FISYCO. Подробности — в политике конфиденциальности.

Держите дизайн-систему и код в согласии

FISYCO связывает библиотеки Figma с кодовой базой, находит расхождения и присылает правку пул-реквестом.

Начать бесплатно

Похожие статьи

4 мин чтения

Инженерия

Как найти зашитые цвета в кодовой базе React

Каждый hex в компоненте выводит его из дизайн-системы. Команды поиска за минуты, ложные срабатывания, которые нужно убрать, и как не пускать новые цвета в код.

5 мин чтения

Инженерия

SVG-спрайт из Figma и когда он лучше компонентов

Один файл символов вместо компонента на каждую иконку: когда SVG-спрайт выгоднее, как подключить его в React и на чём спотыкаются чаще всего.

6 мин чтения

Инженерия

Иконки из Figma в React: весь путь

От компонента-иконки в Figma до влитого pull request: каждый шаг, что он даёт на выходе, и три формата доставки на настоящем сгенерированном коде.