Разделяйте значения и семантические роли
Примитивный токен называет выбранное значение или ступень шкалы, например --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 усложнят будущую тему и заставят компоненты зависеть от внешнего вида вместо назначения.
Учитывайте каскад и наследование
Пользовательские свойства участвуют в каскаде и обычно наследуются. Объявление на :root доступно потомкам, но более близкая декларация может переопределить его по обычным правилам каскада. Регистр важен: --color-text и --Color-text являются разными свойствами. var() подставляет вычисленное значение пользовательского свойства в место использования. Это удобно для локальных тем, но создаёт скрытые зависимости, если компоненты бесконтрольно переопределяют глобальные имена. Глобальный контракт храните на документированной корневой или тематической границе, а приватным компонентным значениям давайте собственный префикс.
:root {
--color-indigo-600: #4f46e5;
--color-slate-950: #020617;
--color-surface: #ffffff;
--color-text: var(--color-slate-950);
--color-action: var(--color-indigo-600);
}Начинайте с намеренно малого набора
Начинайте с ролей, которые уже существуют на реальных экранах, а не генерируйте сотни предполагаемых ступеней. Небольшому продукту обычно нужны две-три поверхности, основной и вторичный текст, граница, одна пара действия, индикатор фокуса и ограниченный набор статусных пар. Запишите потребителей каждого токена. Токен без потребителя, возможно, не нужен в первом контракте. Примитивные шкалы полезны, только если поддерживают настоящие семантические решения. Узкую систему легче проверить во всех темах и состояниях, а расширения можно обсуждать по фактам.
- Пары поверхностей и текста, используемые в реальных макетах.
- Цвета action и on-action для контролов с текстом или значками.
- Границы и focus-ring с подтверждением для состояний.
- Статусные пары только для сообщений, реально существующих в продукте.
Называйте токены по назначению и состоянию
Используйте согласованную иерархию и словарь. Практичная схема — --color-<role> с уточнением важности или состояния, а примитивы можно называть --color-<hue>-<step>. Конкретная форма менее важна, чем отсутствие синонимов primary, brand, accent и action для одной роли. Токены hover, active, disabled, focus или selected вводят лишь при действительно отличающемся значении состояния. Таблица с токеном, допустимым фоном, владельцем и результатом контраста полезнее одной сетки образцов. Если меняется только значение, стабильное имя сохраняют.
| Слой | Пример | Ответственность |
|---|---|---|
| Примитив | --color-indigo-600 | Фиксирует выбранное значение |
| Семантика | --color-action | Описывает роль интерфейса |
| Компонент | --button-background | Адаптирует роль внутри компонента |
| Состояние | --color-action-hover | Описывает подтверждённое состояние |
Продумывайте фолбэки и темы
Второй аргумент var() — фолбэк, используемый, когда ссылка на пользовательское свойство отсутствует или недействительна при подстановке. Это не общий фолбэк поддержки браузера, а запятая может быть частью подставляемого значения. Фолбэки полезны на интеграционной границе, где компонент запускается без темы хоста; повторение сырых значений по всему продукту разрушает централизованный контроль. Темы должны переопределять семантические сопоставления в одной области. Медиа-запрос выбирает предпочтение по умолчанию, а явный выбор приложения задаётся атрибутом или классом с понятным приоритетом. Поэтому в примере для тёмной темы одновременно переопределены поверхность и текст: это явная пара, а не тёмный текст на тёмном фоне.
.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;
}
}Тестируйте сочетания, а не образцы
Цветовой токен не бывает доступным сам по себе. Проверяйте поддерживаемые пары переднего плана и фона после композитинга alpha, включая hover, focus, active, disabled, selected и validation. Фокус нельзя обозначать лишь едва заметной сменой цвета. Снимок строк токенов не доказывает контраст: тестам нужны разрешённые значения и реальные правила сочетания. Проверяйте forced-colors, не пытаясь без причины отменить подстановки пользовательского агента. В документации укажите, является ли токен декоративным, текстовым или относится к нетекстовой графике, поскольку требования различаются.
- Проверьте наследование и локальные переопределения во вложенных компонентах.
- Разрешите каждое поддерживаемое сочетание переднего плана и фона.
- Тестируйте системное предпочтение, явную тему и forced-colors.
- Ищите неиспользуемые, дублирующиеся и устаревшие токены до добавления новых.
Развивайте токены без поломки потребителей
Изменения токенов рассматривайте как изменения API. Найдите потребителей, обновите документацию и снимки, измерьте затронутые пары, а при переименовании публичного токена дайте алиас или миграцию. Удаляйте устаревший алиас лишь после перехода всех потребителей. Канонический источник храните под контролем версий; платформенные представления генерируйте только детерминированным и проверенным способом. Не дублируйте таблицу темы в JavaScript, если ею может владеть CSS. Краткий журнал изменений помогает отличить визуальную корректировку от поломки предположений компонентов.
Первоисточники
Фактические утверждения статьи проверены по этим опубликованным спецификациям.