Тема Tailwind CSS из дизайн‑токенов одной командой

—

от автора

Я сам рисую интерфейсы и сам их верстаю. У меня есть библиотека стилей pbstyles: цвета, размеры и шрифты в ней описаны дизайн‑токенами. Тему для Tailwind я хотел собирать из этих токенов, а не переписывать руками после каждой правки в Figma. Так появился пакет tailwind‑dictionary.

Он берёт токены в JSON и собирает из них тему Tailwind CSS. Светлая и тёмная тема собираются из тех же файлов.

Пакет пригодится, если у вас уже есть токены, но переходить на Tailwind не хочется из‑за того, что тему придётся вести отдельно. Или если вы уже на Tailwind и хотите выгружать переменные из Figma и сразу получать тему.


Почему не попросить ИИ

ИИ может забрать переменные из Figma и собрать тему. Но токены ИИ стоят денег, а тема пересобирается после каждой правки в макете. Тратить их на одну и ту же рутину жалко, лучше оставить на задачи посложнее. К тому же ИИ может каждый раз собрать тему чуть по‑разному.

С пакетом сборку настраиваешь один раз. Дальше это одна команда, и при тех же токенах тема всегда получается одинаковой.

Что умеет

Пакет читает токены в новом формате W3C Design Tokens (DTCG 2025.10) и в старом формате Style Dictionary. Подходит выгрузка из Figma, Tokens Studio и Terrazzo.

Собирает тему для Tailwind 4 и для Tailwind 3. Тем может быть сколько угодно: светлая, тёмная, контрастная, тема второго бренда. В тему попадают цвета, размеры, шрифты, текстовые стили, тени, брейкпоинты и анимации.

Вся настройка — один файл config.json. Редактор подсказывает его поля и подсвечивает ошибки. Есть режим --watch, который пересобирает тему при изменении токенов.

Что отдаёт Figma

Figma выгружает переменные сама: в окне Variables правый клик по коллекции → Export modes. На каждый режим получается файл *.tokens.json.

Возьмём простой пример. Палитра и радиусы лежат в коллекции без режимов. Цвета primary, background и foreground лежат в коллекции с режимами Light и Dark и ссылаются на палитру.

Так выглядит палитра:

{  "color": {    "blue": {      "600": {        "$type": "color",        "$value": {          "colorSpace": "srgb",          "components": [0.149, 0.388, 0.922],          "alpha": 1,          "hex": "#2563EB"        },        "$extensions": { "com.figma.variableId": "VariableID:1:3" }      }    }  },  "radius": {    "4": { "$type": "number", "$value": 4 }  }}

А так режим Light:

{  "color": {    "primary": {      "$type": "color",      "$value": "{color.blue.600}"    }  }}

В CSS это напрямую не вставишь. Цвет записан объектом, primary ссылается на другую переменную, а у радиуса нет единицы: --radius-4: 4 браузер не поймёт. Пакет всё это разбирает сам.

Собираем тему

npm install --save-dev tailwind-dictionary

Палитру кладём в tokens/Primitives.tokens.json, режимы — в tokens/Light.tokens.json и tokens/Dark.tokens.json. Рядом создаём config.json:

{  "version": 4,  "source": ["tokens/Primitives.tokens.json"],  "themes": {    "light": ["tokens/Light.tokens.json"],    "dark": ["tokens/Dark.tokens.json"]  },  "output": "./styles",  "themeAliases": {    "color": "color",    "radius": "radius",    "spacing": "1px"  }}

В source лежат токены, общие для всех тем, в themes — файлы тем. В themeAliases слева ключ темы Tailwind, справа группа токенов. Если в Figma радиусы называются rounded, пишем "radius": "rounded". Строка "spacing": "1px" задаёт шаг отступов: класс p-16 даст ровно 16 пикселей, как в макете.

Запускаем сборку и подключаем результат после Tailwind:

npx tailwind-dictionary
@import 'tailwindcss';@import './styles/tailwind/theme.css';

Теперь в проекте работают классы bg-primary, text-foreground и rounded-4.

Что получилось

Файл theme.css, светлая тема для краткости убрана:

:root {  --theme-color-primary: #2563eb;  --theme-color-background: #f9fafb;  --theme-color-foreground: #111827;}[data-theme='dark'] {  --theme-color-primary: #3380f5;  --theme-color-background: #111827;  --theme-color-foreground: #f9fafb;}@theme {  --*: initial;  --color-*: initial;  --color-blue-400: #3380f5;  --color-blue-600: #2563eb;  --color-gray-50: #f9fafb;  --color-gray-900: #111827;  --radius-0: 0;  --radius-4: 4px;  --radius-8: 8px;  --spacing: 1px;}@theme inline {  --color-primary: var(--theme-color-primary);  --color-background: var(--theme-color-background);  --color-foreground: var(--theme-color-foreground);}

Цвета стали обычными hex, ссылки заменились значениями. Радиус 4 превратился в 4px. Единицу можно поменять опцией numberUnit: "rem" делит число на 16, false оставляет как есть.

Строка --*: initial убирает стандартную тему Tailwind. В проекте остаются только ваши значения, и случайный bg-blue-500, которого нет в макете, просто не соберётся.

Как переключаются темы

Цвета тем лежат в --theme-color-*, а Tailwind ссылается на них через @theme inline. Благодаря inline класс bg-primary компилируется в background-color: var(--theme-color-primary), и значение берётся на том элементе, где стоит класс. Поэтому тему можно поменять в любом месте страницы:

<html data-theme="dark">  <body>    <section data-theme="light">Светлый блок на тёмной странице</section>  </body></html>

Без атрибута светлая и тёмная тема следуют настройкам системы. Атрибут на <html> их перекрывает, это нужно для переключателя темы на сайте.

На что я натыкался

Описания переменных Figma не выгружает. Текстовых стилей и теней в выгрузке тоже нет: в Figma это стили, а не переменные. Я описываю их токенами вручную, и токен типографики h64 превращается в класс text-h64 сразу с размером, интервалом и начертанием.

Файлы тем нельзя класть в source. Если написать tokens/**/*.json, а темы лежат в tokens/themes/, одни и те же токены придут дважды.

Старый формат Style Dictionary пакет тоже читает, но смешивать его с новым в одной сборке нельзя.

Если что‑то не так, ошибка выводится одной строкой — с именем токена и файла:

✘ Token "color.text" (tokens/color.json) references {color.nope}, which is not defined

Попробовать без установки

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

С Figma пакет сам не синхронизируется: выгрузку нужно сделать руками и положить в репозиторий. Компоненты он тоже не собирает, только тему. Если Tailwind в проекте нет, из тех же токенов можно собрать CSS‑переменные и миксины для LESS и SCSS соседним пакетом mixin‑dictionary.

Ссылки

ссылка на оригинал статьи https://habr.com/ru/articles/1101166/