Синий цвет кнопок повторяется в CSS двадцать раз, и чтобы сменить его, надо пройти по всему файлу. С переменными его пишут один раз, а ещё одной строкой меняют для отдельного блока: так делают тёмную тему или особую карточку.
CSS-переменная — свойство с именем, которое начинается с двух дефисов: --accent: #1d4ed8. Объявляют её обычно в :root, чтобы она была видна везде, а читают функцией var(--accent). Переменные наследуются, как цвет текста: блок может переопределить значение, и всё внутри него получит новое.
Пример: светлая и тёмная карточка из одной разметки
Открой пример в песочнице HTML/CSS ссылкой под вторым блоком: разметка попадёт во вкладку index.html, стили — в styles.css. Обе карточки сверстаны одинаково, а вторая стала тёмной потому, что у класса .dark свои значения четырёх переменных.
HTML:
<div class="cards">
<article class="card">
<h2>Светлая карточка</h2>
<p>Цвета и отступы берутся из переменных.</p>
<a class="button" href="#">записаться</a>
</article>
<article class="card dark">
<h2>Тёмная карточка</h2>
<p>Та же разметка: в ней переопределены четыре переменные.</p>
<a class="button" href="#">записаться</a>
</article>
</div>
CSS:
:root {
--bg: #ffffff;
--text: #1f2a44;
--accent: #1d4ed8;
--on-accent: #ffffff;
--space: 1rem;
}
body {
margin: 0;
padding: var(--space);
font-family: system-ui, sans-serif;
}
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
gap: var(--space);
}
.card {
padding: calc(var(--space) * 1.5);
border: 1px solid var(--border, #d6dbe4);
border-radius: 0.75rem;
background: var(--bg);
color: var(--text);
}
.dark {
--bg: #1f2a44;
--text: #f2f4f8;
--accent: #ffb703;
--on-accent: #1f2a44;
}
.button {
display: inline-block;
padding: 0.5rem 1rem;
border-radius: 999px;
background: var(--accent);
color: var(--on-accent);
text-decoration: none;
}
Объявить и прочитать: --имя и var()
Объявление. Переменную задают как обычное свойство, только имя начинается с --: --accent: #1d4ed8. Регистр важен: --accent и --Accent — разные переменные. Значение может быть любым: цвет, размер, целая строка вроде 0 2px 8px rgb(0 0 0 / 15%) для тени.
:root. Это корень документа, тот же html, только с более сильным селектором. Переменные в :root видны на всей странице, поэтому их обычно собирают там в одном месте.
Чтение. var(--accent) подставляет значение туда, где можно было бы написать само значение: background: var(--accent). Имя свойства переменной не бывает: var(--prop): red не сработает.
Запасное значение: var(--имя, запас)
Если переменной нет, var() возьмёт значение после запятой. В примере --border нигде не объявлена, поэтому рамка карточки берёт запасной #d6dbe4:
.card {
border: 1px solid var(--border, #d6dbe4);
}
Без запаса свойство с необъявленной переменной считается недействительным, и элемент получит значение по умолчанию — например, прозрачный фон или унаследованный цвет. Запасное значение тоже может быть переменной: var(--border, var(--text)).
Наследование: переопределение в блоке
Переменная наследуется от родителя к детям, как цвет текста. Поэтому ей можно дать другое значение в любом блоке — и всё внутри блока получит его, а остальная страница останется прежней.
var() берёт ближайшее значение выше по дереву
--accent из :root, а кнопка внутри .dark — значение, переопределённое этим блоком.У класса .dark в примере своё --accent, поэтому кнопка внутри тёмной карточки жёлтая, а у светлой — синяя из :root. Сами правила .card и .button при этом одинаковые для обеих карточек: меняются только значения.
Тёмная тема и prefers-color-scheme
Тема — это набор переменных. Чтобы включить тёмную тему для всей страницы, а не для одной карточки, переменные переопределяют у :root в медиазапросе, который срабатывает, когда в системе выбрана тёмная тема:
@media (prefers-color-scheme: dark) {
:root {
--bg: #1f2a44;
--text: #f2f4f8;
--accent: #ffb703;
--on-accent: #1f2a44;
}
}
Переключатель на сайте делают так же: класс вроде .dark у html переопределяет те же переменные, а скрипт одной строкой ставит или убирает этот класс. Переменные цвета держи парами — фон и текст, акцент и текст на нём, — иначе при смене одной можно получить светлый текст на светлом фоне.
Переменные в calc()
Переменные с размерами удобно умножать: padding: calc(var(--space) * 1.5) даёт полтора базовых отступа. Поменяешь --space в :root — пересчитаются все отступы, которые от неё зависят, и сетка останется согласованной.
Число без единиц тоже можно хранить и превращать в размер: --columns: 3 и width: calc(100% / var(--columns)). А вот склеить единицы из текста не выйдет: var(--size)px не работает, нужно calc(var(--size) * 1px).
Чем это отличается от переменных Sass. Переменные Sass ($accent) существуют только при сборке: в готовом CSS вместо них уже стоят значения. CSS-переменные живут в браузере: их можно переопределить в блоке, в медиазапросе и даже поменять из JavaScript, и страница сразу перерисуется.
Три правки, чтобы закрепить
Открой пример в песочнице и перед каждой правкой предскажи результат:
- Поменяй в
:rootзначение--accentна#2e7d32. Кнопка светлой карточки станет зелёной, а у тёмной останется жёлтой: у.darkсвоё значение. - Убери из
.darkстроку--bg. Тёмная карточка побелеет, а текст в ней останется светлым и почти исчезнет: фон взялся из:root, а цвет текста — из.dark. Поэтому цвета переопределяют парами. - Поставь в
:root--space: 2rem. Все отступы вырастут вдвое: у страницы 32 пикселя, между карточками 32, внутри карточек 48 — они все считаются от одной переменной.
Переменные пригодятся в любой статье этого раздела — например, для отступов из статьи про margin и padding и цветов из статьи про шрифты и цвет текста.