Сайт вебмастера

Базовые приёмы работы с 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-файлы. Красота! :-)

Related Posts