CSS-переменная (custom property) хранит значение под именем, которое начинается с двух дефисов - например, --main-color. Один раз объявив её, можно подставить в любое количество правил через var(--main-color), и при изменении значения переменной обновятся сразу ВСЕ места, где она использована - без поиска и замены по всему файлу.
В этом уроке
Попробуй прямо сейчас: нажимай на кнопки цвета. Заголовок, рамка карточки и фон кнопки - три разных свойства у трёх разных элементов - каждый раз перекрашиваются ОДНОВРЕМЕННО, потому что все они ссылаются на одну и ту же переменную --main-color. Поменялась только она - обновилось всё сразу.
Синтаксис: --имя и функция var()
Объявление переменной выглядит как обычное CSS-свойство, но имя обязательно начинается с двух дефисов: --main-color: #2f5fa8;. Без двух дефисов браузер решит, что это просто неизвестное свойство, и молча его проигнорирует. Чаще всего переменные объявляют внутри псевдокласса :root - он соответствует тегу html и делает переменную доступной абсолютно везде на странице, ведь цвет, который раньше приходилось прописывать HEX-кодом в каждом правиле отдельно, теперь достаточно задать один раз. Использовать переменную можно только через функцию var(): color: var(--main-color);.
:root { --gap: 16px; } .card { padding: var(--gap); margin: var(--gap); } - одно число используется сразу в двух местах.
:root { main-color: blue; } .title { color: main-color; } - без двух дефисов и без var() это не переменная, а невалидный CSS, который браузер просто пропустит.
Наследование и каскад: почему это не Sass-переменные
CSS-переменные легко перепутать с переменными из препроцессоров вроде Sass или Less, но разница принципиальная. Sass-переменная - это просто текстовая подстановка, которая происходит один раз при СБОРКЕ проекта: браузер её вообще не видит, получая уже готовое статичное значение. CSS-переменная, наоборот, живёт прямо в браузере и вычисляется в реальном времени - а значит, участвует в обычном каскаде и наследовании. Если переопределить переменную внутри вложенного селектора, новое значение будет действовать только там и внутри, не трогая остальную страницу - обычное поведение каскада, ничего специального.
:root { --accent: blue; } .dark-section { --accent: white; } - внутри .dark-section переменная станет белой, снаружи так и останется синей.
ожидание, что переменная, объявленная внутри .card, будет доступна СНАРУЖИ этого блока - переменные наследуются только ВНИЗ по дереву элементов, не вверх и не в стороны.
var() с запасным значением
У функции var() есть необязательный второй аргумент - запасное значение, которое применяется, если указанной переменной не существует вообще: var(--main-color, black). Это удобно, когда правило может использоваться в разных местах сайта, и не везде гарантированно объявлена нужная переменная - вместо того чтобы стиль просто "сломался", применится разумное значение по умолчанию.
color: var(--accent, #333); - если --accent нигде не объявлена, применится #333, а не пустое значение.
color: var(--accent); без запасного значения - если переменная не определена, свойство color попросту не применится (в спецификации это называется "invalid at computed-value time").
Частые ошибки новичков
| Ошибка | Что происходит | Как исправить |
|---|---|---|
| Забывают два дефиса в начале имени переменной | Браузер не распознаёт запись как переменную и просто игнорирует правило | Имя переменной всегда начинается с двух дефисов: --main-color |
| Ждут, что CSS-переменные ведут себя как Sass-переменные | Не понимают, почему значение меняется в разных местах страницы по-разному | Помнить, что CSS-переменные - часть каскада и наследования, а не текстовая подстановка при сборке |
| Объявляют переменную в узком селекторе, а используют её снаружи | Переменная "не находится" - применяется значение по умолчанию или ничего не применяется | Общие переменные объявлять в :root, а не в случайном классе |
| Не указывают запасное значение в var(), когда переменная может отсутствовать | Свойство просто не применяется, если переменной не оказалось | Указывать var(--имя, запасное-значение) там, где переменная не гарантирована |
Шпаргалка
| Что нужно | Как сделать |
|---|---|
| Объявить переменную | --имя: значение; (два дефиса обязательны) |
| Использовать переменную | var(--имя) |
| Запасное значение | var(--имя, запасное-значение) |
| Сделать переменную глобальной | Объявить внутри :root { ... } |
| Переопределить локально | Указать то же имя переменной внутри вложенного селектора |
| Отличие от Sass/Less | CSS-переменные живут в браузере, участвуют в каскаде, меняются на лету |
Коротко: отвечаем на вопросы
Чем CSS-переменные отличаются от переменных Sass или Less?
Sass и Less - препроцессоры: их переменные превращаются в статичные значения один раз при сборке проекта, браузер их вообще не видит. CSS-переменные живут прямо в браузере, вычисляются в реальном времени, участвуют в каскаде и наследовании, и их можно менять на лету через JavaScript, :hover или медиа-запросы.
Обязательно ли объявлять переменные внутри :root?
Нет, переменную можно объявить в любом селекторе - тогда она будет доступна только внутри него и его потомков. :root удобен именно тем, что соответствует тегу html и делает переменную доступной практически везде на странице, поэтому для общих, часто используемых значений его выбирают по умолчанию.
Что будет, если переменная не определена, а запасного значения в var() нет?
Свойство, использующее такую переменную, просто не применится - браузер не станет подставлять вместо него ничего по умолчанию. Чтобы избежать этого, стоит указывать второй аргумент var(): var(--имя, запасное-значение).