ADR-UI-004. Стилевая система и токены
Нужна единая токен-система: согласованный набор цветов, интервалов, типографики — один
источник, никаких «магических» значений по компонентам. Механизм — CSS custom properties --icd-*
(без препроцессорной палитры), что заодно открывает runtime-смену темы. Значения токенов ведём в
ui/tokens/.
1. Контекст и постановка задачи
Любому компоненту набора ui/ нужен единый источник цветов, интервалов и типографики — чтобы
менять тему в одной точке и не плодить разрозненные значения по компонентам. Механизм
зафиксирован предыдущими ADR: CSS custom properties --icd-* в ui/tokens/, SCSS для
организации, без внешнего UI-kit (ADR-UI-001, ADR-UI-003). Здесь фиксируется модель токенов
(структура, именование, правила).
2. Драйверы решения
- Один источник значений — никаких HEX/«магических» px в компонентах; всё через токены.
- Явная семантика — компонент выражает намерение («цвет ссылки», «фон primary-кнопки»), а не сырой тон.
- Переносимость — токены живут в
ui/tokens/и едут вместе с набором; набор самодостаточен. - Runtime-темизация — CSS custom properties позволяют переопределять тему без пересборки (тёмная тема / тема под клиента — на будущее).
3. Рассмотренные варианты
- A. Препроцессорная палитра (Less/SCSS-миксины, значения на этапе сборки) — не даёт runtime-темизации, значения «зашиты» в собранный CSS.
- B. CSS custom properties, двухуровневые (примитивы + семантика) — runtime-темизация,
явная семантика, самодостаточный набор токенов в
ui/tokens/. - C. Захардкоженные значения / utility-фреймворк — рвёт единый источник и семантику, чужая шкала.
4. Решение
Выбран Вариант B. Токены — CSS custom properties с префиксом --icd-*, двухуровневые:
слой примитивов и слой семантики. Значения ведём в ui/tokens/; по правилу одного факта в этом
ADR фиксируются структура, именование и правила, а не полный перечень значений.
4.1. Двухуровневая модель токенов
- Примитивы — «сырые» значения ролей и шкалы тонов. Роли:
primary,neutral,danger,success,warning,info,green. Шкала тонов100…1000(заполняется по факту использования). Пример:--icd-primary-600: #5c6bf2;. - Семантические токены — назначение поверх примитивов, через
var(...). Группы:text/bg/brd/icon(общие и семантические),control(кнопки: типыprimary/secondary/tertiary/ghost/danger/inverse× состоянияdefault/hover/click/disable/focus),form(поле/метка/значение/маска по состояниям),link. Пример:--icd-color-text-link-default: var(--icd-primary-600);.
Компоненты ui/ обращаются к токенам через var(--icd-...) — по возможности к семантическим
(не к примитивам напрямую), чтобы смысл был явным.
4.2. Именование токенов
Дефисная нотация с префиксом --icd-, по шаблону «группа → уточнения → состояние»:
| Тип | CSS custom property | Пример значения |
|---|---|---|
| Примитив цвета | --icd-primary-600 |
#5c6bf2 |
| Семантика цвета | --icd-color-text-link-default |
var(--icd-primary-600) |
| Control (кнопка) | --icd-color-bg-control-primary-default |
var(--icd-primary-500) |
| Интервал | --icd-space-x2 |
8px (шкала на базе 4px) |
| Радиус | --icd-radius-field |
8px |
| Высота | --icd-size-height-button |
32px |
| Focus | --icd-border-focus |
4px |
4.3. Типографика
Шрифтовые примитивы — CSS-переменные: семейства --icd-font-header, --icd-font-body;
размеры, веса (400/500/600), межстрочные интервалы. Текстовые стили (заголовки H1…H5,
body M/S/XS в вариантах веса) оформляются как SCSS-миксины/utility-классы набора — композитный
стиль удобнее миксином, чем одной CSS-переменной.
4.4. Структура ui/tokens/
app/ui/tokens/
├── _primitives.scss # :root { --icd-<role>-<tone>: … } — цвета-примитивы + база
├── _semantic.scss # :root { --icd-color-…: var(--icd-…) } — семантика
├── _typography.scss # шрифтовые переменные + миксины текстовых стилей
├── _spacing.scss # --icd-space-x*, радиусы, высоты, border, focus
└── index.scss # агрегатор (подключается из src/styles.scss)
4.5. Кастомизация под проект
Значения по умолчанию лежат в ui/tokens/. Переопределение (тёмная тема, тон под клиента) —
в src/styles/theme/ переопределением значений тех же переменных (ADR-UI-003), без правки
компонентов.
4.6. Правила
- Значения — только в
ui/tokens/(и переопределения вstyles/theme/). В компонентах — толькоvar(--icd-...). - Никаких HEX / «магических» px в стилях компонентов и inline-стилях.
- Предпочитать семантические токены примитивам в компонентах.
- Интервалы — только
--icd-space-*; радиусы/высоты/типографика — соответствующие токены/миксины.
5. Положительные следствия
- Смена темы/тона — одна точка (
ui/tokens/илиstyles/theme/), компоненты не трогаются. - Явная семантика токенов делает стили компонентов читаемыми и устойчивыми к смене палитры.
- CSS custom properties дают runtime-темизацию (тёмная тема и пр.) без пересборки.
- Токены самодостаточны в
ui/tokens/— набор переносится без внешних стилевых зависимостей.
6. Отрицательные следствия и компромиссы
- Двухуровневость (примитивы + семантика) — больше файлов/имён, чем плоская палитра; окупается явностью смысла.
- Дисциплина: каждый варьируемый параметр компонента должен идти через токен, а не литерал.
- Шкала тонов заполняется по факту — часть ступеней долгое время пустует.
7. Проверка
- В стилях компонентов нет HEX-литералов и «магических» px (grep по
#[0-9a-fA-F]{3,}и голым px внеui/tokens/). - Смена значения
--icd-primary-600перекрашивает primary-акценты во всём приложении без других правок. - Каждый компонент
ui/использует толькоvar(--icd-...)для цветов, интервалов и типографики.
8. Открытые вопросы / отложено
- Подключение шрифтов (self-host / источник, набор начертаний) — уточнить при первой реализации.
- Тёмная тема / тема под клиента — возможна на CSS custom properties; реализация отложена до требования.