Урок · CSS
CSS-переменные: единые значения без копирования
CSS-переменная (пользовательское свойство) — значение с именем, которое начинается с двух дефисов: --color-accent: #2a62d9. Её объявляют один раз, обычно в :root, и подставляют функцией var(--color-accent). Поменял значение в одном месте — изменилось везде.
- Объявление:
--имя: значение;, использование:var(--имя). - В
:root— глобальные значения, в селекторе компонента — локальные. - Переменные наследуются потомками и могут переопределяться.
var(--x, запас)— запасное значение, если переменная не задана.- Тёмная тема — это переопределение тех же переменных.
Зачем этот урок сейчас
Фирменный синий встречается в стилях двадцать раз: в кнопках, ссылках, рамках. Дизайнер меняет оттенок — и начинается поиск по файлу. С переменными цвет, отступы и радиусы живут в одном месте, тёмная тема делается несколькими строками, а у компонентов появляются варианты без дублирования правил.
Объявление и использование
:root {
--color-accent: #2a62d9;
--color-text: #171923;
--space-m: 16px;
--radius: 12px;
}
.button {
background: var(--color-accent);
padding: 12px var(--space-m);
border-radius: var(--radius);
}
a { color: var(--color-accent); }Имя начинается с -- и чувствительно к регистру: --Accent и --accent — разные переменные. :root — корневой элемент страницы (тег html), поэтому объявленные там переменные доступны везде.
Наследование и область видимости
.card { --tone: #2f5d50; border-left: 4px solid var(--tone); }
.card.is-warning { --tone: #c7442f; }
.card .title { color: var(--tone); } /* потомок видит переменную карточки */Переменная работает как обычное наследуемое свойство: её видит сам элемент и все потомки, но не соседи. Поэтому вариант компонента делается одной строкой — модификатор меняет только значение переменной, а правила, которые её читают, остаются прежними.
Запасное значение
.hero {
padding-top: var(--header-height, 64px);
color: var(--hero-text, var(--color-text, #000));
}Второй аргумент var() подставляется, если переменная не объявлена. Запасные значения можно вкладывать. Но запас не спасает от опечатки в значении: если --space-m: 16 px (с пробелом), переменная объявлена, а свойство, которое её использует, станет недействительным и вернётся к значению по умолчанию.
Светлая и тёмная тема
:root {
--bg: #ffffff;
--text: #171923;
--muted: #617087;
--line: #e5e8ef;
}
@media (prefers-color-scheme: dark) {
:root { --bg: #14161f; --text: #e8eaf2; --muted: #a0a8bb; --line: #2a2f3d; }
}
[data-theme="dark"] { --bg: #14161f; --text: #e8eaf2; --muted: #a0a8bb; --line: #2a2f3d; }
body { background: var(--bg); color: var(--text); }Компоненты читают var(--bg) и не знают, какая сейчас тема. Тема — это просто другой набор значений: по системной настройке через медиазапрос или по выбору человека через атрибут на html. Проверь, что в тёмной теме поменялись и фон, и текст, и рамки.
Как называть переменные
| Плохо | Хорошо | Почему |
|---|---|---|
--blue | --color-accent | акцент может стать зелёным |
--gray2 | --color-muted | имя говорит о роли |
--16 | --space-m | шкала размеров |
--header | --header-height | понятно, что хранит |
Называй по роли, а не по виду: когда в тёмной теме серый станет другим серым, имя --color-muted останется правдой.
Переменные и calc()
:root { --space: 8px; }
.section { padding: calc(var(--space) * 4); } /* 32px */
.grid { gap: calc(var(--space) * 2); } /* 16px */
.page { padding-top: calc(var(--header-height, 64px) + 16px); }Внутри calc() переменные складываются и умножаются. Так из одной базовой величины строится вся шкала отступов, и интерфейс выглядит согласованно.
Переменные и JavaScript
const root = document.documentElement;
root.style.setProperty('--color-accent', '#c7442f'); // задать
getComputedStyle(root).getPropertyValue('--color-accent').trim(); // прочитать
slider.addEventListener('input', () => {
root.style.setProperty('--font-scale', slider.value);
});JavaScript меняет переменные, и все стили, которые их читают, обновляются сами. Это удобно для темы, размера шрифта или величины с ползунка. Обычные состояния (открыто, выбрано) по-прежнему удобнее переключать классами.
Где переменные не работают
Переменную нельзя использовать в условии медиазапроса (@media (min-width: var(--md)) не сработает) и в имени свойства или селектора. Значение подставляется как есть, поэтому var(--size)px не превратится в «16px» — нужно хранить единицы в самой переменной или писать calc(var(--size) * 1px).
CSS-переменные или переменные Sass
Живут в браузере: наследуются, переопределяются в компонентах и медиазапросах, меняются из JavaScript.
Исчезают при сборке, подставляясь значением. Нельзя поменять в браузере или переопределить для потомков.
Частые ошибки
- Называют переменные по цвету:
--blue2. - Объявляют переменную в блоке, а читают в соседнем, который её не наследует.
- Пишут
var(--size)pxи ждут единицы измерения. - Ставят переменную в условие медиазапроса.
- Выносят в переменные значения, которые нигде не повторяются.
Доступность
Переменные упрощают доступность: контрастную или тёмную тему можно сделать переопределением набора цветов. Проверяй контраст текста и фона в каждой теме — не ниже 4.5:1 для основного текста. Если позволяешь менять размер шрифта переменной, задавай его в rem, чтобы учитывались и настройки браузера.
Поддержка браузерами
CSS-переменные поддерживаются всеми актуальными браузерами с 2017 года. Правило @property, которое задаёт переменной тип и делает её анимируемой, тоже доступно в современных версиях браузеров.
Практика
Создай переменные для фона, текста, акцента, радиуса и базового отступа. Сделай из них карточку и кнопку. Добавь вариант карточки .card--warning, который меняет только переменную акцента, и тёмную тему по атрибуту data-theme="dark" на html.
Показать решение
:root {
--bg: #fff; --text: #171923; --accent: #2a62d9;
--radius: 12px; --space: 8px;
}
[data-theme="dark"] { --bg: #14161f; --text: #e8eaf2; --accent: #7aa2ff; }
body { background: var(--bg); color: var(--text); }
.card {
padding: calc(var(--space) * 3);
border-radius: var(--radius);
border-left: 4px solid var(--accent);
}
.card--warning { --accent: #c7442f; }
.button {
background: var(--accent);
color: #fff;
padding: var(--space) calc(var(--space) * 2);
border-radius: var(--radius);
}Частые вопросы
Где объявлять CSS-переменные?
Глобальные — в :root, локальные — в селекторе компонента. Переменная доступна элементу и всем его потомкам.
Что будет, если переменная не объявлена?
Подставится запасное значение из второго аргумента var(). Если запаса нет, свойство вернётся к значению по умолчанию.
Можно ли использовать переменную в медиазапросе?
Внутри блока медиазапроса — да, в самом условии — нет.
Как изменить CSS-переменную из JavaScript?
document.documentElement.style.setProperty('--имя', 'значение'). Все стили, которые читают переменную, обновятся.
Чем CSS-переменные лучше переменных Sass?
Они работают в браузере: наследуются, переопределяются для отдельных компонентов и тем, меняются из JavaScript. Переменные Sass исчезают при сборке.