Базовые приёмы работы с CSS-переменными
28-07-2020Reading time ~ 7 min.CSS, HTML, LESS, SASS 5158
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-файлы. Красота! :-)