01

Разделяйте значения и семантические роли

Примитивный токен называет выбранное значение или ступень шкалы, например --color-indigo-600. Семантический токен называет роль интерфейса, например --color-action. Сопоставление семантического токена с примитивом создаёт одно контролируемое звено косвенности. Компоненты используют --color-action и не знают, соответствует ли ему в текущей теме индиго, синий или другой проверенный цвет. Такое разделение упрощает аудит: примитивы проверяют на происхождение и корректность конвертаций, а семантические пары — на контраст и смысл состояния. Не раскрывайте каждый цвет палитры всем компонентам без реальной задачи.

Семантика не означает субъективный маркетинговый термин. Предпочтительны проверяемые роли: surface, surface-raised, text, text-muted, border, focus-ring, action, on-action, danger и on-danger. Для каждого токена переднего плана документируют фоны, на которых он поддерживается. Имя light-gray не сообщает отношений и гарантий использования; контракт text-muted может точно назвать допустимые поверхности. Не встраивайте текущую тему в семантическое имя. Названия white-text и dark-surface усложнят будущую тему и заставят компоненты зависеть от внешнего вида вместо назначения.

02

Учитывайте каскад и наследование

Пользовательские свойства участвуют в каскаде и обычно наследуются. Объявление на :root доступно потомкам, но более близкая декларация может переопределить его по обычным правилам каскада. Регистр важен: --color-text и --Color-text являются разными свойствами. var() подставляет вычисленное значение пользовательского свойства в место использования. Это удобно для локальных тем, но создаёт скрытые зависимости, если компоненты бесконтрольно переопределяют глобальные имена. Глобальный контракт храните на документированной корневой или тематической границе, а приватным компонентным значениям давайте собственный префикс.

css
:root {
  --color-indigo-600: #4f46e5;
  --color-slate-950: #020617;
  --color-surface: #ffffff;
  --color-text: var(--color-slate-950);
  --color-action: var(--color-indigo-600);
}
03

Начинайте с намеренно малого набора

Начинайте с ролей, которые уже существуют на реальных экранах, а не генерируйте сотни предполагаемых ступеней. Небольшому продукту обычно нужны две-три поверхности, основной и вторичный текст, граница, одна пара действия, индикатор фокуса и ограниченный набор статусных пар. Запишите потребителей каждого токена. Токен без потребителя, возможно, не нужен в первом контракте. Примитивные шкалы полезны, только если поддерживают настоящие семантические решения. Узкую систему легче проверить во всех темах и состояниях, а расширения можно обсуждать по фактам.

  • Пары поверхностей и текста, используемые в реальных макетах.
  • Цвета action и on-action для контролов с текстом или значками.
  • Границы и focus-ring с подтверждением для состояний.
  • Статусные пары только для сообщений, реально существующих в продукте.
04

Называйте токены по назначению и состоянию

Используйте согласованную иерархию и словарь. Практичная схема — --color-<role> с уточнением важности или состояния, а примитивы можно называть --color-<hue>-<step>. Конкретная форма менее важна, чем отсутствие синонимов primary, brand, accent и action для одной роли. Токены hover, active, disabled, focus или selected вводят лишь при действительно отличающемся значении состояния. Таблица с токеном, допустимым фоном, владельцем и результатом контраста полезнее одной сетки образцов. Если меняется только значение, стабильное имя сохраняют.

СлойПримерОтветственность
Примитив--color-indigo-600Фиксирует выбранное значение
Семантика--color-actionОписывает роль интерфейса
Компонент--button-backgroundАдаптирует роль внутри компонента
Состояние--color-action-hoverОписывает подтверждённое состояние
05

Продумывайте фолбэки и темы

Второй аргумент var() — фолбэк, используемый, когда ссылка на пользовательское свойство отсутствует или недействительна при подстановке. Это не общий фолбэк поддержки браузера, а запятая может быть частью подставляемого значения. Фолбэки полезны на интеграционной границе, где компонент запускается без темы хоста; повторение сырых значений по всему продукту разрушает централизованный контроль. Темы должны переопределять семантические сопоставления в одной области. Медиа-запрос выбирает предпочтение по умолчанию, а явный выбор приложения задаётся атрибутом или классом с понятным приоритетом. Поэтому в примере для тёмной темы одновременно переопределены поверхность и текст: это явная пара, а не тёмный текст на тёмном фоне.

css
.button {
  color: var(--color-on-action, #ffffff);
  background: var(--color-action, #4f46e5);
  border: 1px solid var(--color-border, #cbd5e1);
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-surface: #0f172a;
    --color-text: #f8fafc;
  }
}
06

Тестируйте сочетания, а не образцы

Цветовой токен не бывает доступным сам по себе. Проверяйте поддерживаемые пары переднего плана и фона после композитинга alpha, включая hover, focus, active, disabled, selected и validation. Фокус нельзя обозначать лишь едва заметной сменой цвета. Снимок строк токенов не доказывает контраст: тестам нужны разрешённые значения и реальные правила сочетания. Проверяйте forced-colors, не пытаясь без причины отменить подстановки пользовательского агента. В документации укажите, является ли токен декоративным, текстовым или относится к нетекстовой графике, поскольку требования различаются.

  • Проверьте наследование и локальные переопределения во вложенных компонентах.
  • Разрешите каждое поддерживаемое сочетание переднего плана и фона.
  • Тестируйте системное предпочтение, явную тему и forced-colors.
  • Ищите неиспользуемые, дублирующиеся и устаревшие токены до добавления новых.
07

Развивайте токены без поломки потребителей

Изменения токенов рассматривайте как изменения API. Найдите потребителей, обновите документацию и снимки, измерьте затронутые пары, а при переименовании публичного токена дайте алиас или миграцию. Удаляйте устаревший алиас лишь после перехода всех потребителей. Канонический источник храните под контролем версий; платформенные представления генерируйте только детерминированным и проверенным способом. Не дублируйте таблицу темы в JavaScript, если ею может владеть CSS. Краткий журнал изменений помогает отличить визуальную корректировку от поломки предположений компонентов.

S

Первоисточники

Фактические утверждения статьи проверены по этим опубликованным спецификациям.

  1. W3C CSS Custom Properties for Cascading Variables Level 1