оригинальная версия версия для слабовидящих контрастная версия выключить изображения включить изображения тёмная версия светлая версия RSS FEED K2 NEWS
☰☰
Четверг, 15 Октябрь 2026 00:00

CSS-переменные: переключаем тему сайта без препроцессоров

Тёмная тема на сайте у меня ассоциировалась исключительно с Sass или Less — заводишь переменные, компилируешь два набора стилей, подключаешь нужный файл через класс на body. Работало, но каждое изменение цветовой палитры означало пересборку проекта, а на простом Joomla-шаблоне без сборщика это вообще превращалось в отдельный квест. Пока не разобрался, что почти всё то же самое давно умеет чистый CSS — через custom properties, без единого препроцессора.

Механика предельно простая. Объявляете переменные в блоке :root — это псевдокласс, который указывает на корневой элемент документа (html), а значит, значения будут доступны на любой странице сайта:


:root {
  --bg-color: #ffffff;
  --text-color: #1f2d27;
  --accent-color: #2e8b57;
  --border-color: #dbe6e0;
}

Дальше вместо конкретных цветов в остальных стилях подставляете функцию var():


body {
  background: var(--bg-color);
  color: var(--text-color);
}
a { color: var(--accent-color); } .card { border: 1px solid var(--border-color); }

На этом этапе разницы с обычными hex-кодами вроде бы никакой — разве что теперь цвет меняется в одном месте, а не ищется по всему файлу. Но настоящая сила custom properties раскрывается, когда нужно эти значения переопределить для другого состояния — например, для тёмной темы.

Переопределяются переменные ровно так же, как задавались, только уже в другом селекторе:


[data-theme="dark"] {
  --bg-color: #1a1f1c;
  --text-color: #e8efe9;
  --accent-color: #5fbf8a;
  --border-color: #2e3a34;
}

А переключение темы сводится к одной строчке JavaScript, которая ставит атрибут data-theme на <html>:

document.documentElement.setAttribute('data-theme', 'dark');

Всё остальное — вообще без дополнительного кода. Браузер сам пересчитывает var(--bg-color) во всех местах, где эта переменная используется, потому что CSS-переменные, в отличие от Sass-переменных, живут не на этапе компиляции, а прямо во время работы страницы в браузере. Sass-переменная — это просто текстовая подстановка перед сборкой, а custom property — реальное значение в дереве стилей, которое можно читать и менять в любой момент через JavaScript, и именно это отличие делает возможным мгновенное переключение темы без перезагрузки страницы и без повторной генерации CSS-файлов.

Есть и более простой сценарий без JS вообще — автоматическое подключение тёмной темы по системным настройкам пользователя через медиа-запрос prefers-color-scheme:


@media (prefers-color-scheme: dark) {
  :root {
    --bg-color: #1a1f1c;
    --text-color: #e8efe9;
  }
}

Такой вариант удобен как базовый уровень, но у него есть минус — пользователь не может вручную переключить тему на сайте, если его системная настройка не совпадает с тем, что ему хочется видеть именно на вашем ресурсе. На практике я обычно комбинирую оба подхода: prefers-color-scheme задаёт тему по умолчанию, а атрибут data-theme, выставляемый вручную через переключатель и запоминаемый в localStorage, позволяет пользователю переопределить системный выбор.

Отдельно стоит сказать про поддержку в старых браузерах — custom properties не работают в Internet Explorer вообще ни в каком виде, но для 2026 года это уже не проблема, доля IE в статистике посещений давно стремится к нулю. Все современные браузеры (Chrome, Firefox, Safari, Edge) поддерживают переменные без всяких полифиллов ещё с 2017 года.

Ещё один практический момент — переменные можно объявлять не только в :root, но и в любом селекторе, и тогда область их действия ограничивается этим блоком и его потомками. Это удобно для локальных «тем» внутри одного компонента — например, у карточки с акцентным цветом можно задать собственную переменную, не трогая глобальную палитру:


.card--warning {
  --accent-color: #d9822b;
}

Всё, что внутри .card--warning использует var(--accent-color), автоматически подхватит оранжевый оттенок, а остальной сайт при этом останется зелёным.

Из подводных камней — если забыть указать запасное значение вторым аргументом в var(), а переменная почему-то не определена (опечатка в имени, например), свойство просто не применится вообще, без явной ошибки в консоли, которую было бы легко заметить. Спасает вторым параметром — var(--accent-color, #2e8b57) — тогда при отсутствии переменной браузер подставит запасной цвет, и вёрстка не «поедет» молча.

Спонсоры: