Токены DTCG
Новый формат токенов W3C Design Tokens 2025.10 — $value, $type, миксины из typography, брейкпоинтов и keyframes без поля mixin.
DTCG — стандарт формата дизайн-токенов от W3C Design Tokens Community Group. Служебные поля пишутся с $: $value, $type, $description, $extensions. С версии 1.6 mixin-dictionary читает его без дополнительной настройки.
Формат определяется автоматически: если в токенах есть $value, сборка идёт в режиме DTCG. В одной сборке используется один формат — смешивать value и $value в одном наборе файлов (включая файлы тем) нельзя, это ошибка.
Простые токены
$type можно задать один раз на группу — вложенные токены его наследуют. Ссылки пишутся как {group.token} и обязаны вести на существующий токен.
{
"color": {
"$type": "color",
"white": { "$value": { "colorSpace": "srgb", "components": [1, 1, 1], "hex": "#ffffff" } },
"black": { "$value": "#000000" }
},
"font": {
"family": {
"$type": "fontFamily",
"sans": { "$value": ["Inter", "sans-serif"] }
}
}
}$color-white: #ffffff;
$color-black: #000000;
$font-family-sans: 'Inter', sans-serif;| Значение DTCG | Результат |
|---|---|
dimension { "value": 16, "unit": "px" } | 16px |
color { "colorSpace": "srgb", "components": […], "hex": "#…" } | #…; с alpha — rgba(…); другие цветовые пространства — oklch(…), color(display-p3 …) |
fontFamily ["Inter", "sans-serif"] | 'Inter', sans-serif |
shadow (объект или массив слоёв) | 0px 1px 2px 0px #000000, inset … |
duration, cubicBezier, border, transition | 150ms, cubic-bezier(…), 1px solid #…, … |
| строка | как есть |
Неизвестный $type не останавливает сборку: пакет выведет предупреждение с путём токена.
Миксины без поля mixin
Миксины собираются из структуры токенов:
- составной
typography→ миксин с именем токена:font.h64→h64, переменные<путь>-font-size,<путь>-line-heightи так далее; - категории из
mediaAliases:screen.lg.{min,max}→ миксин медиазапросаlg. Нужны дочерние токеныminи/илиmax: одиночныйscreen.smостаётся переменной; - категории из
keyframesAliases:keyframes.<имя>.<кадр>.<свойство>→ keyframes<имя>.
{
"font": {
"size": { "$type": "dimension", "64": { "$value": { "value": 64, "unit": "px" } } },
"leading": { "$type": "number", "tight": { "$value": 1.25 } },
"weight": { "$type": "fontWeight", "bold": { "$value": 700 } },
"h64": {
"$type": "typography",
"$value": {
"fontSize": "{font.size.64}",
"lineHeight": "{font.leading.tight}",
"fontWeight": "{font.weight.bold}"
}
}
},
"screen": {
"$type": "dimension",
"lg": {
"min": { "$value": { "value": 921, "unit": "px" } },
"max": { "$value": { "value": 1440, "unit": "px" } }
}
},
"keyframes": {
"$type": "number",
"show": {
"from": { "opacity": { "$value": 0 } },
"to": { "opacity": { "$value": 1 } }
}
}
}SCSS:
$font-h64-font-size: 64px;
$font-h64-line-height: 1.25;
$font-h64-font-weight: 700;
$screen-lg-min: 921px;
$screen-lg-max: 1440px;
$keyframes-show-from-opacity: 0;
$keyframes-show-to-opacity: 1;
@mixin h64 {
font-size: $font-h64-font-size;
line-height: $font-h64-line-height;
font-weight: $font-h64-font-weight;
}
@mixin lg {
@media all and (min-width: $screen-lg-min) and (max-width: $screen-lg-max) {
@content;
}
}
@include keyframes(show) {
from {
opacity: $keyframes-show-from-opacity;
}
to {
opacity: $keyframes-show-to-opacity;
}
}LESS:
@font-h64-font-size: 64px;
@font-h64-line-height: 1.25;
@font-h64-font-weight: 700;
@screen-lg-min: 921px;
@screen-lg-max: 1440px;
.h64() {
font-size: @font-h64-font-size;
line-height: @font-h64-line-height;
font-weight: @font-h64-font-weight;
}
.lg(@rules) {
@media all and (min-width: @screen-lg-min) and (max-width: @screen-lg-max) {
@rules();
}
}
.keyframes(show, { from { opacity: @keyframes-show-from-opacity; } to { opacity: @keyframes-show-to-opacity; } });В CSS текстовый стиль раскладывается на отдельные переменные: --font-h64-font-size, --font-h64-line-height, --font-h64-font-weight.
Имя миксина через $extensions
{
"font": {
"body": {
"$type": "typography",
"$extensions": { "dev.prosazhin.mixin": "body-lg" },
"$value": { "fontSize": "16px", "lineHeight": 1.5 }
}
}
}Получится миксин body-lg. Старое поле mixin тоже работает и имеет наивысший приоритет.
Переход со старого формата
Было (value) | Стало (DTCG) |
|---|---|
{ "value": "#ffffff" } | { "$value": "#ffffff" } + "$type": "color" на группе |
{ "value": "16px" } | { "$value": { "value": 16, "unit": "px" } } + "$type": "dimension" |
"font-size", "line-height" с "mixin": "h64" | один токен "$type": "typography" с объектом в $value |
screen.lg.min с "mixin": "lg" | screen.lg.min без mixin |
keyframes.show.from.opacity с "mixin": "show" | тот же путь без mixin |
"mixin": "body-lg" для особого имени | "$extensions": { "dev.prosazhin.mixin": "body-lg" } |
Имена переменных после перехода не меняются: font.h64 с fontSize даёт ту же $font-h64-font-size, что токен font.h64.font-size в старом формате. Простые токены можно перевести утилитой convertToDTCG из style-dictionary/utils, а составные типы дописать вручную. Готовый пример — токены pbstyles.
Перевод не обязателен: старый формат продолжает работать.