Сервис развивается: тестируем формат, собираем идеи, улучшаем сервис. Есть идеи?

Написать
Войти
Дайджесты
Современное темизирование в CSS

Современное динамическое темизирование компонентов с помощью функций CSS

Объединение новых CSS-функций light-dark() и contrast-color() с контейнерными style-запросами и типизированными свойствами @property позволяет верстальщикам создавать полностью автоматические цветовые схемы и адаптивные интерфейсные компоненты без дублирования стилей и использования скриптов JavaScript.

Современное динамическое темизирование компонентов с помощью функций CSS

Создание адаптивных и поддерживаемых дизайн-систем долгое время требовало от веб-разработчиков написания сложного кода. Глобальные темы (темная и светлая) реализовывались либо через дублирование стилей в медиа-запросах @media (prefers-color-scheme), либо с помощью JavaScript, который динамически переключал CSS-классы на корневом элементе страницы. Ситуация становилась еще сложнее, когда требовалось реализовать локальное темизирование — например, покрасить отдельную карточку в фирменные цвета бренда так, чтобы весь вложенный в нее текст автоматически адаптировал свой цвет для сохранения читаемости (контрастности).

Современный стандарт CSS предоставляет набор нативных инструментов, которые позволяют полностью разделить глобальное макро-темизирование (уровень всей страницы) и локальное микро-темизирование (уровень отдельных изолированных компонентов). Объединение четырех возможностей спецификации — @property (типизированные пользовательские свойства), light-dark() (функция выбора цвета на основе текущей цветовой схемы), contrast-color() (автоматический расчет контрастного цвета текста) и @container style() (контейнерные запросы по стилям) — позволяет верстать гибкие интерфейсы без единой строчки JavaScript.


Макро-темизирование с помощью функции light-dark()

Функция light-dark(color1, color2) возвращает первый цвет, если на элементе активна светлая цветовая схема (color-scheme: light), и второй цвет, если активна темная схема (color-scheme: dark).

Главное преимущество этого подхода перед традиционными медиа-запросами заключается в поддержке наследования. Вместо отслеживания системных настроек пользователя на глобальном уровне, функция light-dark() ориентируется на вычисленное браузером значение свойства color-scheme для конкретного элемента DOM-дерева.

Как настроить макро-темизирование страницы:
  1. Инициализация: Укажите на корневом элементе страницы (html или :root) поддержку обеих схем. Это заставит браузер автоматически считывать системные предпочтения пользователя.
  2. Переопределение: При необходимости принудительно включить темную тему для определенного блока страницы (например, боковой панели управления), просто задайте для него color-scheme: dark. Все вложенные элементы автоматически переключат цвета своих функций light-dark().
  3. Объявление переменных: Создайте глобальные переменные для фонов и текста, используя новую функцию.
/* Базовая настройка темы в файле стилей */
:root {
  color-scheme: light dark; /* Включает автоматическое переключение схем */
  
  --bg-primary: light-dark(#ffffff, #121212);
  --text-primary: light-dark(#212121, #eeeeee);
}

body {
  background-color: var(--bg-primary);
  color: var(--text-primary);
}

Подробные примеры работы и синтаксис функции описаны в справочном руководстве MDN на странице developer.mozilla.org.


Микро-темизирование: Управление контрастом с contrast-color()

На уровне компонентов часто возникает задача создания гибких карточек. Если у карточки динамически меняется фоновый цвет (например, задается администратором в панели управления), разработчик должен гарантировать, что текст поверх этого фона останется читаемым.

Для этого в CSS вводится функция contrast-color(color). Браузер автоматически вычисляет степень яркости переданного фонового цвета и подбирает для него максимально контрастный цвет текста (по умолчанию — черный или белый), удовлетворяющий стандартам доступности интерфейсов.

Инструкция по созданию адаптивного компонента:
  1. Задайте фоновый цвет компонента (это может быть бренд-цвет компании).
  2. Задайте цвет текста с помощью функции автоматического подбора.
  3. Важное ограничение: Браузер возвращает только чистый черный (#000000) или чистый белый (#ffffff) цвет. Если дизайн-система требует использования мягких оттенков (например, кремового цвета на темном фоне), используйте результаты contrast-color() как маркер (детектор) в контейнерных запросах.
/* Использование contrast-color в карточке */
.brand-card {
  --card-bg: #e0f7fa; /* Динамический цвет фона */
  
  background-color: var(--card-bg);
  color: contrast-color(var(--card-bg)); /* Браузер сам выберет черный или белый */
}

Контейнерные запросы по стилям для мягкой темизации

Чтобы применить сложные цветовые палитры вместо контрастного черного/белого текста, используется механизм контейнерных запросов по стилям (@container style()). Описание принципов работы этой технологии доступно в блоге разработчиков Chrome на странице developer.chrome.com.

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

Процедура настройки адаптивной палитры:
  1. Зарегистрируйте типизированное свойство --contrast-color в CSS-файле.
  2. Задайте это свойство для компонента, используя результат функции автоподбора контраста.
  3. Опишите правила ветвления с помощью @container style(): если вычисленное значение контрастного цвета является белым, значит, фон под ним достаточно темный — примените к тексту мягкий теплый оттенок. Если черным — примените глубокий темный тон.
/* Регистрация переменной */
@property --contrast-color {
  syntax: '<color>';
  inherits: true;
  initial-value: black;
}

.brand-card {
  --contrast-color: contrast-color(var(--card-bg));
  container-type: normal;
}

/* Ветвление на основе вычисленного контраста */
@container style(--contrast-color: white) {
  .brand-card .card-description {
    color: oklch(0.95 0.02 80); /* Мягкий светлый оттенок для темного фона */
  }
}

@container style(--contrast-color: black) {
  .brand-card .card-description {
    color: oklch(0.25 0.02 240); /* Глубокий темный оттенок для светлого фона */
  }
}

Процесс верификации и тестирования темы

Для подтверждения качества и доступности созданной темы разработчику необходимо выполнить следующие действия:

  1. Проверка системного переключения: Переключите тему в настройках операционной системы (или с помощью эмуляции в панели разработчика DevTools). Убедитесь, что глобальный фон страницы адаптировался мгновенно и без задержек.
  2. Проверка локального переопределения: Убедитесь, что блоки с жестко заданной схемой color-scheme: dark сохраняют темный вид даже при активной светлой теме ОС, а вложенный текст корректно наследует стили.
  3. Стресс-тест контрастности: Сгенерируйте набор карточек с экстремальными фоновыми цветами (очень светлые, очень темные, пастельные, неоновые). Пройдите по ним визуально и с помощью автоматических плагинов проверки доступности (например, Lighthouse или Axe). Убедитесь, что коэффициент контрастности текста (Contrast Ratio) везде соответствует стандарту WCAG AA (не менее 4.5:1).
  4. Тестирование теней и свечений (Elevation): В светлой теме элементы должны выделяться с помощью теней (box-shadow), цвет которых становится прозрачным в темной схеме. В темной теме для создания ощущения глубины используйте легкие светящиеся рамки, вычисляемые из цвета фона с помощью относительного синтаксиса цветов (oklch(from var(--bg) ...)).
  5. Внимание к ограничениям: На момент подготовки статьи экспериментальная функция @function имеет крайне ограниченную поддержку в браузерах. Избегайте использования @function для построения критически важных элементов интерфейса без предоставления надежных резервных вариантов (fallbacks).