Токены — это роли, а не цвета

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

Любая дизайн-система приходит к одной развилке, обычно на второй неделе. Палитра есть. Назвать токен --blue-500 — или назвать его --primary?

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

Ломается не выбор, а переименование

Назовите токен значением — и значение записано дважды: в имени и в объявлении. В первый день они согласны. Согласие кончается при первой смене значения.

css
/* Первый день: честно. */
--blue-500: #4f46e5;

/* Четвёртый месяц: бренд теперь зелёный. */
--blue-500: #16a34a;

Никто его не переименует. Переименование означает правку каждого места использования, а места использования — это ровно то, чего не хотелось трогать: ради этого токен и заводили. И теперь код говорит «синий», а имеет в виду «зелёный», и каждый новый человек читает имя и ему верит.

У роли такой поломки нет, потому что роль — не утверждение о значении:

css
--gr-primary: #16a34a;

Переименовывать нечего. Токен всё это время говорил «цвет основного действия», и цвет основного действия изменился — ровно то, ради чего токены существуют.

Роли ходят парами, и в этом весь смысл

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

Цветовой токен сам по себе не говорит, что кладётся поверх него. Пара ролей — говорит:

css
--gr-primary: #4f46e5;
--gr-primary-fg: #ffffff;

У каждой поверхности есть передний план, и пара — это контракт: красите этим — пишете тем. Контракт можно проверить. Портал проверяет 16 пар токенов в обеих темах на каждой сборке, и считает их функцией самой библиотеки, а не своей копией формулы: две реализации одного правила однажды разойдутся, и в день, когда это случится, никто не заметит.

С --blue-500 проверять нечего. Есть синий и надежда.

Три слоя

Плоский список тоже не выживает. Здесь токены — три группы с тремя разными задачами: 104 токена фундамента и 93 ролевых токена. Звучит много, пока не видно, зачем слои:

  1. Фундамент. Сырые шкалы: палитра, отступы, радиусы, кегли. Они названы значениями, и это правильно: --gr-slate-500 — факт о цвете, а не решение об интерфейсе.
  2. Роли. Из чего сделан интерфейс: --gr-bg, --gr-fg, --gr-primary, --gr-danger — и у каждой свой передний план. Роли ссылаются на фундамент. Приложение видит только этот слой.
  3. Производные. Состояния, посчитанные от ролей: hover, active, disabled, кольцо фокуса. Считаются формулой, а не глазом, чтобы новый цвет бренда давал верный hover без чьего-либо выбора.

Слои и делают правдой «переименовывать нечего». Ребрендинг переписывает первый слой и часть второго. Третий следует по построению, а компоненты — которые называли только роли — не меняются вовсе.

Где правило заканчивается

Два честных ограничения: правило без них — лозунг.

Роли не выживают, когда их слишком мало. Если всё --gr-primary, вы построили не систему ролей, а одну переменную. Признак — компонент, которому приходится локально перебивать токен, чтобы выглядеть верно: этот перебой и есть роль, которую не назвали. Эти ролевые токены лежат в 8 группах, и их число росло, а не падало, по мере того как компоненты становились честнее.

Покомпонентные токены — не провал ролевого слоя. Максимальная ширина панели дропдауна не роль: ни у чего больше в интерфейсе нет о ней мнения. Загнав её в глобальный слой, вы превратите глобальный слой в ящик со всяким. Правило уже, чем кажется: глобальные токены — это роли. Локальным разрешено быть конкретными, и называться они должны по компоненту, которому принадлежат.

Проверка

Чтобы понять, роли ли ваши токены, не читайте их. Сделайте другое: переименуйте цвет бренда в уме и посчитайте вранье.

Возьмите --blue-500 и представьте, что бренд позеленел. Каждое вхождение слова «blue» в коде — ложь, с которой вы согласились жить. Возьмите --gr-primary и сделайте то же. Считать нечего.

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

Компоненты в статье