Базовые приёмы работы с CSS-переменными
28-07-2020Время чтения ~ 7 мин.CSS, HTML, LESS, SASS 6166
CSS-переменные всё больше используются при вёрстке, поскольку это довольно удобный инструмент, упрощающий работу. Если не оглядываться на IE11, то css-переменные хорошо поддерживаются браузерами. Во всяком случае все современные версии без проблем.
Концепция css-переменных очень проста, но судя по многочисленным публикациям, она создаёт определённое непонимание на уровне «а зачем они вообще нужны?». Как правило верстка выполняется с помощью препроцессоров (в первую очередь Sass). Но поскольку там уже есть свои переменные, то становится непонятно, зачем ещё создавать их на уровне CSS.
Ответ кроется в понимании того, для кого именно создаются css-переменные и какое у них будет назначение.
CSS vs Sass
Тут всё просто. Если вы пишите на Sass какой-то конкретный дизайн, то скорее всего css-переменные будут не нужны. Sass-переменные с лихвой покроют все потребности вёрстки. Если что-то изменилось, то меняем scss-файл, компилируем и всё прекрасно работает.
Так мы делаем уже много лет, и здесь ничего не меняется.
CSS only
Предположим вы сделали html/css-компонент, который состоит из нескольких классов и имеет какое-то определённое оформление. Например в красных оттенках. Чтобы использовать этот же модуль в другом дизайне, например в синем, потребуется его переписывать. Логично было бы вынести эти изменяемые свойства в переменные.
То есть подход к CSS-переменным должен быть точно таким же как и в других языках программирования. И он состоит их двух вещей:
- нужно объявить переменную
- можно её использовать
В итоге любой «css-модуль» должен состоять как бы из двух частей: блок переменных и непосредственный блок css-кода (там где переменные используются).
Объявление css-переменных
Есть два разных подхода к тому, где именно объявлять css-переменные. Но вначале нужно уяснить, что они не только «переменные», но и обычные атрибуты CSS. Поэтому их действие также подчинено каскадной иерархии. То есть переменная родительского блока будет доступна в дочерних.
<style>
.my1 {
--mybg: blue;
}
.my2 {
background-color: var(--mybg);
}
</style>
<div class="my1">
Блок 1
<div class="my2">
Блок 2
</div>
</div>
<div class="my2">
Блок 3
</div>
Мы объявляем переменную --mybg в блоке .my1. Поскольку первый .my2 вложен в .my1, то фон будет синим. А второй .my2 не вложен, поэтому для него не будет фона, поскольку переменная --mybg там не определена.
Этот пример позволяет понять первый подход к определению css-переменных — выносить переменные на более высокий уровень иерархии html-документа. Обычно это тэг BODY (поскольку все остальные уже вложенные в него), либо специальный псевдоэлемент :root.
Если мы объявим переменную так:
<style>
:root {
--mybg: blue;
}
.my2 {
background-color: var(--mybg);
}
</style>
то все .my2 окажутся с синим фоном.
Такой подход порождает проблемы, с которой ведут борьбу программисты всех языков: замусоривание глобальной области видимости и сложность с именованием переменных.
Скажем у нас будет несколько блоков, где нужно поменять цвет фона. Это приводит к тому, что необходимо заботится о правильном именовании переменных.
:root {
--mybg1: red;
--mybg2: green;
--mybg3: blue;
}
Это, конечно, создаёт определённую путаницу и усложняет код. Но, если проект небольшой, то такой подход, когда root-переменные объявляются отдельно, может служит примером замены аналогичного Sass-проекта.
То есть когда вы пишите самостоятельный html/css-компонент, есть смысл вынести его настройки в css-переменные, чтобы менять только их.
Другой подход к объявлению переменных — противоположный: переменная объявляется в пределах блока, который её будет использовать. Для сложных html-компонентов это может быть контейнер, а для простых — для указанного css-класса.
Очень часто встречается такой вариант:
.my {
--bg: red;
background-color: var(--bg);
}
На самом деле он бессмыслен, поскольку в одном блоке и задаётся переменная и тут же используется. Единственным способом изменить цвет фона блока будет использование style:
<div class="my" style="--bg: green">
Блок 1
</div>
В некоторых случаях блок .my разбивают на две части: переменная объявляется где-то вверху кода, а основные стили уже внизу файла. Но такой вариант элементарно неудобный, поэтому лучше всего использовать возможность указывать дефолтное значение для необъявленной переменной.
.my {
background-color: var(--bg, red);
}
Если --bg не существует, то будет использовано значение по умолчанию (red).
Такой подход является универсальным, поскольку сочетает в себе плюсы каскадности и удобство использования.
Область видимости
От того, где именно будет объявлена переменная зависит очень многое. Частая практика объявления в :root создаёт проблему конфликта имён переменных.
Например у нас есть два блока:
.my1 { color: var(--color, red); }
.my2 { color: var(--color, green); }
Мы видим, что в обоих блоках используется переменная --color, что вполне логично для пределов своего блока. Но если мы объявим эту переменную выше в :root, то она окажется одной и той же сразу для двух блоков.
Чтобы избегать таких конфликтов, переменные следует изменять в пределах своего блока:
.my1 { --color: orange; }
.my2 { --color: yellow; }
...
.my1 { color: var(--color, red); }
.my2 { color: var(--color, green); }
Если css-переменная используется только в каком-то классе или html-тэге, то не нужно выносить её определение на более высокий уровень.
Стилизация через дополнительный класс
Пусть у нас объявлен класс:
.my1 {
color: var(--color, red);
... и много других свойств
}
Предположим что этот класс какой-то сложный, состоящий из множества свойств и определяющий какое-то поведение, например hover. Что бы его изменить, мы можем переопределить css-переменные, например в :root, что изменит сам класс .my1. Но что делать, если нам нужно сохранить этот дефолтный класс как есть, но на его основе сделать еще один (два, три — сколько угодно)? Традиционно это делается за счёт создания новых классов, которые будут повторять все те же свойства, что и у «родительского».
Но с помощью css-переменных задача решается намного проще. Мы можем создать новый класс, содержащий только css-переменные, которые использует «родительский».
.my1 {
color: var(--color, red);
... и много других свойств
}
.my2 {
--color: blue;
... и другие переменные
}После этого достаточно лишь указать второй класс, чтобы получить новую стилизацию.
<div class="my1 my2">текст</div>
Данный приём позволяет создавать новые классы на основе существующих, используя только их css-переменные. Это позволяет стилизовать любые классы через дополнительные.
CSS и Sass
При использовании Sass появляется еще больше возможностей так как объединяются две технологии. Общий принцип можно свести к одному примеру:
$red: #f01010;
.my {
background-color: var(--bg, $red);
}
Там где нужно изменить цвет через Sass, меняется sass-переменная, а если нужно изменить через CSS, то используется css-переменная.
Область применения css-переменных
Конечно достоинства css-переменных проявляются в тех проектах, которые предполагают некоторую смену дизайна. Например слайдер, где нужно поменять цвет dot-элементов навигации. Если они вынесены css-переменными, то сделать это проще, чем менять css или sass-код.
Другой пример — вариативность какого-то одного класса на одной странице. Например в Berry 3 будет класс .badge для оформления «бэйджиков». Основные настройки делаются через css-классы:

Text<span class="badge bg-blue600 t-gray100 t80">88</span>
Но у такого элемента очень большая вариативность: можно ещё менять ширину бордюра и его стиль, а также разные скругления. Если делать для этого отдельные классы, то это существенно раздует css-файл, при том, что элемент не самый востребованный.
С помощью css-переменных проблема решается элементарно:

Text<span class="badge bg-blue600 t-gray100 t80" style="--badge-bor-style: dotted">88</span> Text<span class="badge bg-blue600 t-gray100 t80" style="--badge-bor-width: 5px">88</span> Text<span class="badge bg-blue600 t-gray100 t80" style="--badge-bor-width: 3px; --badge-bor-style: double">88</span> Text <span class="badge bg-blue600 t-gray100 t80 bor-none" style="--badge-radius: 5px">88</span>
Ещё одно применение css-переменных: задание базовых значений типографики. Опять же, пример из Berry 3:
body {
font-size: var(--body-size-base, $body-size-base);
font-family: var(--body-font-family, $body-font-family);
line-height: var(--body-line-height, $body-line-height);
color: var(--body-color, $body-color);
text-align: var(--body-text-align, $body-text-align);
background-color: var(--body-bg, $body-bg);
}
При использовании готового css-файла из дистрибутива фреймворка, чтобы изменить общий шрифт или фон, потребовалось бы переопределить их в отдельном css-файле. Через css-переменные это делается более элегантно:
<style>
body {
--body-size-base: 17px;
--body-font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
--body-line-height: 1.7;
--body-color: #555;
}
</style>
Прелесть такого подхода ещё в том, что блок style можно разместить на любой странице сайта и тем самым сделать её уникальной. При этом не нужно править ни css, ни scss-файлы. Красота! :-)