Перейти к содержанию
Innovialabs

CSS-переменные (custom properties): практическое руководство

Основы3 мин

Коротко

CSS-переменная объявляется как --имя: значение и читается через var(--имя); она наследуется по дереву, может меняться в медиазапросах и из JavaScript прямо в браузере, чем и отличается от переменных Sass.

Короткий ответ

CSS-переменные (официально — custom properties) позволяют один раз задать значение и использовать его во многих местах. Объявление начинается с двух дефисов, чтение — через функцию var():

:root {
  --color-primary: #7c3aed;
  --space-m: 1rem;
}

.button {
  background: var(--color-primary);
  padding: var(--space-m);
}

Изменили значение в одном месте — изменились все кнопки. В отличие от переменных препроцессоров, CSS-переменные живут в браузере: их можно переопределять для отдельных блоков, в медиазапросах и из JavaScript.

Синтаксис: что важно знать

  • Имя начинается с -- и чувствительно к регистру: --Color и --color — разные переменные.
  • Значением может быть почти что угодно: цвет, размер, список шрифтов, часть значения.
  • Переменная — это свойство, поэтому объявляется внутри селектора, а не на верхнем уровне файла.
  • Переменную нельзя использовать в условии медиазапроса (@media (min-width: var(--bp)) не сработает) и в имени свойства.

Область видимости и наследование

Custom properties подчиняются обычному каскаду и наследуются потомками. Переменная, объявленная на :root, доступна везде. Объявленная на компоненте — только внутри него.

:root { --accent: #7c3aed; }

.card--warning { --accent: #d97706; }

.card .title { color: var(--accent); }

Все заголовки карточек фиолетовые, а у карточки-предупреждения — оранжевые. Компонент читает одну и ту же переменную, а «настраивается» снаружи. Это основа гибких компонентов без десятка модификаторов.

Переопределять можно и в медиазапросах:

:root { --gutter: 1rem; }
@media (min-width: 768px) {
  :root { --gutter: 2rem; }
}

Запасные значения

Второй аргумент var() — fallback, он используется, если переменная не определена:

.box { padding: var(--box-padding, 1rem); }

Нюанс: fallback не спасает от некорректного значения. Если --size: red, а вы пишете width: var(--size), свойство станет недействительным и получит значение по умолчанию, а не fallback. Браузер не подсветит это как ошибку синтаксиса, поэтому такие баги проверяйте в DevTools.

Изменение из JavaScript

Это главное преимущество перед Sass: значения меняются на лету.

const root = document.documentElement;

// записать
root.style.setProperty('--accent', '#5b21b6');

// прочитать
const accent = getComputedStyle(root).getPropertyValue('--accent').trim();

Типичные сценарии: позиция курсора для эффектов, прогресс прокрутки, пользовательский выбор цвета, переключение темы. JS меняет одну переменную, а вся логика отображения остаётся в CSS.

Дизайн-токены и темы

Дизайн-токены — именованные решения дизайн-системы: цвета, отступы, радиусы, тени. CSS-переменные — удобный способ донести их до кода. Хорошая практика — два уровня:

  1. Базовые токены — палитра: --violet-600, --gray-100.
  2. Смысловые токены — роли: --color-bg, --color-text, --color-accent.

Компоненты используют только смысловые. Тогда тёмная тема — это переопределение нескольких ролей:

:root {
  --color-bg: #ffffff;
  --color-text: #1a1033;
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #120b24;
    --color-text: #ede9fe;
  }
}

[data-theme="dark"] {
  --color-bg: #120b24;
  --color-text: #ede9fe;
}

Чем отличаются от переменных Sass

Переменные SassCSS-переменные
Когда вычисляютсяПри сборкеВ браузере
Видны в итоговом CSSНет, заменяются значениямиДа
Каскад и наследованиеНетДа
Меняются из JS и медиазапросовНетДа
Работают в условиях @mediaДаНет

Они не конкуренты. Sass удобен для вычислений на этапе сборки и миксинов, CSS-переменные — для всего, что должно меняться во время работы.

Частые ошибки

  • Заводить переменную для каждого значения — их становится сотни, и никто не помнит, какая за что отвечает.
  • Называть по виду (--purple) там, где нужна роль (--color-accent), — при смене бренда имя начинает врать.
  • Не учитывать пробелы: в некоторых браузерах getPropertyValue возвращает значение с пробелом в начале — добавляйте trim().

Частые вопросы

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

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.