CSS-переменные (custom properties): практическое руководство
Основы3 минРедакция Innovialabs
Коротко
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-переменные — удобный способ донести их до кода. Хорошая практика — два уровня:
- Базовые токены — палитра:
--violet-600,--gray-100. - Смысловые токены — роли:
--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
| Переменные Sass | CSS-переменные | |
|---|---|---|
| Когда вычисляются | При сборке | В браузере |
| Видны в итоговом CSS | Нет, заменяются значениями | Да |
| Каскад и наследование | Нет | Да |
| Меняются из JS и медиазапросов | Нет | Да |
Работают в условиях @media | Да | Нет |
Они не конкуренты. Sass удобен для вычислений на этапе сборки и миксинов, CSS-переменные — для всего, что должно меняться во время работы.
Частые ошибки
- Заводить переменную для каждого значения — их становится сотни, и никто не помнит, какая за что отвечает.
- Называть по виду (
--purple) там, где нужна роль (--color-accent), — при смене бренда имя начинает врать. - Не учитывать пробелы: в некоторых браузерах
getPropertyValueвозвращает значение с пробелом в начале — добавляйтеtrim().