От автора: переменные CSS (также известные как пользовательские свойства) поддерживаются в веб-браузерах почти четыре года. Мне нравится использовать их в зависимости от проекта и ситуации. Они очень полезны и просты в использовании, но часто фронтенд-разработчик может неправильно их использовать или неправильно понимать.
Основная цель этой статьи заключается в том, что я хотел создать место, где я мог бы выгрузить всю информацию, которую я знаю о переменных CSS, и даже узнать больше и задокументировать их. Вы узнаете все, что вам нужно знать о переменных CSS, с множеством примеров и вариантов использования. Вы готовы? Давайте приступим.
Вступление
Переменные CSS — это значения, которые определены в документе CSS с целью повторного использования и уменьшения избыточных значений в CSS. Вот простой пример.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
.section { border: 2px solid #235ad1; } .section-title { color: #235ad1; } .section-title::before { content: ""; display: inline-block; width: 20px; height: 20px; background-color: #235ad1; } |
В этом фрагменте значение #235ad1 используется трижды. Представьте себе это для большого проекта, разных файлов CSS, и вас попросили изменить цвет. Лучшее, что вы можете сделать — это старое доброе «Найти и заменить».
С помощью переменных CSS это можно сделать намного лучше. Давайте узнаем, как их определить. Во-первых, вам нужно добавить двойной дефис —перед именем переменной. Для начала мы определим переменную в элементе :root или html.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
:root { --color-primary: #235ad1; } .section { border: 2px solid var(--color-primary); } .section-title { color: var(--color-primary); } .section-title::before { /* Other styles */ background-color: var(--color-primary); } |
Разве это не намного чище, чем предыдущий фрагмент? Переменная —color-primary является глобальной, потому что мы определили ее в элементе :root. Однако мы также можем ограничить переменные определенными элементами по всему документу.
Именование переменных
Подобно именованию переменных в языках программирования, именование переменных CSS ничем не отличается. Допустимое имя переменной CSS должно содержать буквенно-цифровые символы, подчеркивания и дефисы. Также стоит отметить, что переменные CSS чувствительны к регистру.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/* Valid names */ :root { --primary-color: #222; --_primary-color: #222; --12-primary-color: #222; --primay-color-12: #222; } /* Invalid names */ :root { --primary color: #222; /* Spacings are not allowed */ --primary$%#%$# } |
Определение области видимости
Что полезно в переменных CSS, так это то, что мы можем их ограничивать. Концепция похожа на то, что у нас есть в других языках программирования. Возьмем, к примеру, Javascript.
1 2 3 4 5 6 |
let element = "cool"; function cool() { let otherElement = "Not cool"; console.log(element); } |
Переменная element является глобальной, поэтому к ней можно получить доступ внутри функции cool(). Однако доступ к переменной otherElement возможен только внутри функции cool(). Давайте применим данную концепцию к переменным CSS.
1 2 3 4 5 6 7 8 |
:root { --primary-color: #235ad1; } .section-title { --primary-color: d12374; color: var(--primary-color); } |
Переменная —primary-color является глобальной, и к ней можно получить доступ из любого элемента документа. Если переопределить ее в пределах объявления блока .section-title, ее новое значение работает только там. Вот наглядный пример, который лучше это объясняет.
У нас —primary-color используется для цвета заголовка раздела. Мы хотим настроить цвет для разделов избранных авторов и последних статей, поэтому мы переопределяем ее. То же самое касается и переменной —unit. Вот версия CSS для рисунка выше.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/* Global variables */ :root { --primary-color: #235ad1; --unit: 1rem; } /* Section title default color and spacing */ .section-title { color: var(--primary-color); margin-bottom: var(--unit); } /* Overrding the section title color */ .featured-authors .section-title { --primary-color: #d16823; } /* Overrding the section title color & spacing */ .latest-articles .section-title { --primary-color: #d12374; --unit: 2rem; } |
Предоставление резервного варианта
Резервный вариант не означает откат к браузеру, который не поддерживает переменные CSS. Резервный вариант — это функция, которую мы можем использовать с переменными CSS. Рассмотрим следующий пример:
1 2 3 |
.section-title { color: var(--primary-color, #222); } |
Обратите внимание, что у var() несколько значений. Второй #222 будет работать только в том случае, если переменная —primary-color по какой-то причине не определена. Мало того, мы можем вложить одну var() в другую.
1 2 3 |
.section-title { color: var(--primary-color, var(--black, #222)); } |
Эта функция может быть полезна в случаях, когда значение переменной зависит от определенного действия. Когда переменная не имеет значения, важно предоставить для нее запасной вариант.
Случаи использования и примеры
Управление размером компонента
Обычно в дизайн-системе для кнопки используется несколько размеров. Кнопка бывает трех размеров (маленький, нормальный, большой). Реализовать это с помощью переменных CSS было не так просто.
1 2 3 4 5 6 7 8 9 10 11 12 |
.button { --unit: 1rem; padding: var(--unit); } .button--small { --unit: 0.5rem; } .button--large { --unit: 1.5rem; } |
Изменяя переменную —unit в пределах компонента кнопки, мы создали различные варианты кнопки.
Переменные CSS и цвета HSL
HSL означает оттенок, насыщенность, яркость. Значение оттенка определяет цвет. Значения насыщенности и яркости могут определять, насколько темным или светлым будет цвет.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
:root { --primary-h: 221; --primary-s: 71%; --primary-b: 48%; } .button { background-color: hsl(var(--primary-h), var(--primary-s), var(--primary-b)); transition: background-color 0.3s ease-out; } /* Making the background darker */ .button:hover { --primary-b: 33%; } |
Обратите внимание, как я сделал кнопку темнее, уменьшив значение переменной —primary-b.
Пропорциональное изменение размера
Если вы работали с такой дизайнерской программой, как Photoshop, Sketch, Figma или Adobe XD, то у вас может возникнуть идея удерживать клавишу Shift при изменении размера элемента, чтобы не искажать его.
В CSS нет прямого способа сделать это, но у нас есть простой обходной путь, который использует, как вы уже догадались, переменные CSS.
Предположим, что есть иконка, ширина и высота которой должны быть равны. Я определил переменную —size и использовал ее как для ширины, так и для высоты.
1 2 3 4 5 |
.icon { --size: 22px; width: var(--size); height: var(--size); } |
Вот и все! Теперь вы можете имитировать эффект изменения размера через Shift, изменяя только значение переменной —size.
CSS-сетка
Переменные CSS могут быть чрезвычайно полезны для сеток. Представьте, что вы хотите, чтобы в контейнере сетки отображались дочерние элементы на основе определенной предпочтительной ширины. Вместо того, чтобы создавать класс для каждого варианта и дублировать CSS, это проще сделать с помощью переменных.
1 2 3 4 5 6 7 8 9 10 |
.wrapper { --item-width: 300px; display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--item-width), 1fr)); grid-gap: 1rem; } .wrapper-2 { --item-width: 500px; } |
Благодаря этому мы можем создать полную сеточную систему, которая будет гибкой, простой в обслуживании и может использоваться в других проектах. То же самое можно применить и к свойству grid-gap.
1 2 3 4 5 6 7 8 9 10 |
.wrapper { --item-width: 300px; --gap: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--item-width), 1fr)); } .wrapper.gap-1 { --gap: 16px; } |
Хранение полных значений
Градиенты CSS
Под полными значениями я имею в виду, например, градиент. Если у вас есть градиент или фон, которые используются в системе, их можно сохранить в переменной CSS.
1 2 3 4 5 6 7 |
:root { --primary-gradient: linear-gradient(150deg, #235ad1, #23d1a8); } .element { background-image: var(--primary-gradient); } |
Или мы можем сохранить одно значение. Возьмем, к примеру, угол наклона.
1 2 3 4 5 6 7 8 |
.element { --angle: 150deg; background-image: linear-gradient(var(--angle), #235ad1, #23d1a8); } .element.inverted { --angle: -150deg; } |
Положение фона
Мы можем включить несколько значений в переменную CSS, и это полезно в случае, если у нас есть элемент, который необходимо позиционировать по-разному в зависимости от конкретного контекста.
1 2 3 4 5 6 7 |
.table { --size: 50px; --pos: left center; background: #ccc linear-gradient(#000, #000) no-repeat; background-size: var(--size) var(--size); background-position: var(--pos); } |
Переключение между темным и светлым режимами
Темный и светлый режимы сейчас востребованы для веб-сайтов как никогда. С помощью переменных CSS мы можем хранить две их версии и переключаться между ними в зависимости от предпочтений пользователя или системы.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
:root { --text-color: #434343; --border-color: #d2d2d2; --main-bg-color: #fff; --action-bg-color: #f9f7f7; } /* A class added to the <html> element*/ .dark-mode { --text-color: #e9e9e9; --border-color: #434343; --main-bg-color: #434343; --action-bg-color: #363636; } |
Установка значения по умолчанию
В некоторых случаях вам нужно будет установить переменную CSS с помощью JavaScript. Предположим, нам нужно получить height расширяемого компонента.
Переменная пуста —details-height-open, и она будет добавлена к определенному элементу HTML. Он будет содержать значение в пикселях. Когда по какой-либо причине Javascript не работает, важно указать правильное значение по умолчанию или резервное значение.
1 2 3 |
.section.is-active { max-height: var(--details-height-open, auto); } |
Значение auto является резервным значением в случае, если Javascript не дает сбой, и переменная CSS не определена —details-height-open.
Управление шириной оболочки
Оболочка веб-сайта может иметь несколько вариантов. Вам может понадобиться небольшая оболочка для одной страницы и более крупная для другой. В таком случае может быть полезно включение переменных CSS.
1 2 3 4 5 6 7 8 |
.wrapper { --size: 1140px; max-width: var(--size); } .wrapper--small { --size: 800px; } |
Встроенные стили
Использование переменных CSS со встроенными стилями может открыть множество новых возможностей, о которых вы, возможно, не знали. Возможно, это не идеальный подход для производственных веб-сайтов, но он может быть полезен для создания прототипов и тестирования различных идей.
Элементы динамической сетки
Мы можем добавить переменную —item-width внутри атрибута стиля, и все. Такой подход может помочь, например, в создании прототипов сеток.
1 2 3 4 5 |
<div class="wrapper" style="--item-width: 250px;"> <div></div> <div></div> <div></div> </div> |
1 2 3 4 5 |
.wrapper { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--item-width), 1fr)); grid-gap: 1rem; } |
Аватарки пользователей
Еще один полезный вариант использования — определение размеров элементов. Допустим, вам нужно четыре разных размера аватара пользователя с возможностью контролировать их размер с помощью только одной переменной.
1 2 3 4 |
<img src="user.jpg" alt="" class="c-avatar" style="--size: 1" /> <img src="user.jpg" alt="" class="c-avatar" style="--size: 2" /> <img src="user.jpg" alt="" class="c-avatar" style="--size: 3" /> <img src="user.jpg" alt="" class="c-avatar" style="--size: 4" /> |
1 2 3 4 5 |
.c-avatar { display: inline-block; width: calc(var(--size, 1) * 30px); height: calc(var(--size, 1) * 30px); } |
Давайте проанализируем приведенный выше CSS:
Во-первых, у нас есть var(—size, 1). Я добавил резервное значение на случай, если значение не было добавлено в атрибут стиля.
Минимальный размер аватара 30px * 30px.
Медиа-запросы
Комбинирование переменных CSS и медиа-запросов может быть очень полезным для настройки переменных, которые используются на всем веб-сайте. Самый простой пример, о котором я могу подумать — это изменение значения интервала.
1 2 3 4 5 6 7 8 9 |
:root { --gutter: 8px; } @media (min-width: 800px) { :root { --gutter: 16px; } } |
Для любого элемента, использующего переменную —gutter, будет изменен интервал в зависимости от размера области просмотра. Разве это не круто?
Наследование
Да, переменные CSS наследуются. Если в родительском элементе определена переменная CSS, то дочерние элементы наследуют ту же переменную CSS. Возьмем следующий пример.
1 2 3 |
<div class="parent"> <p class="child"></p> </div> |
1 2 3 4 5 6 7 |
.parent { --size: 20px; } .child { font-size: var(--size); } |
Элемент .child будет иметь доступ к переменной —sizeв результате наследования от родителя. Интересно, а? Вам может быть любопытно, как мы можем извлечь из этого пользу? Что ж, вот пример из жизни.
У нас есть группа действий со следующими требованиями:
Возможность изменять размер всех элементов, изменяя только одну переменную
Интервал должен быть динамическим (уменьшается при уменьшении размера элемента и увеличивается при увеличении размера элемента).
1 2 3 4 5 |
<div class="actions"> <div class="actions__item"></div> <div class="actions__item"></div> <div class="actions__item"></div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
.actions { --size: 50px; display: flex; gap: calc(var(--size) / 5); } .actions--m { --size: 70px; } .actions__item { width: var(--size); height: var(--size); } |
Обратите внимание, как я использовал переменную —size для свойства flexbox gap. Это означает, что интервал может быть динамическим и будет зависеть от переменной —size. Другой полезный пример — использование наследования переменных CSS для настройки анимации CSS.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@keyframes breath { from { transform: scale(var(--scaleStart)); } to { transform: scale(var(--scaleEnd)); } } .walk { --scaleStart: 0.3; --scaleEnd: 1.7; animation: breath 2s alternate; } .run { --scaleStart: 0.8; --scaleEnd: 1.2; animation: breath 0.5s alternate; } |
Таким образом, нам не нужно определять @keyframes два раза, и оно будет наследовать настроенную переменную CSS для элементов .walk и .run.
Как работает валидация переменных CSS
Когда переменная CSS внутри функции var() недопустима, браузер заменяет ее начальным или унаследованным значением в зависимости от используемого свойства.
1 2 3 4 5 6 7 |
:root { --main-color: 16px; } .section-title { color: var(--main-color); } |
Я использовал значение 16px для свойства color. Это совершенно неверно. Поскольку свойство color наследуется, браузер сделает следующее:
Наследуется ли свойство?
Если да, то есть ли у родителя свойство?
Да: наследует значение
Нет: устанавливает значение по умолчанию
Если нет, установит начальное значение
Вот блок-схема, объясняющая работу браузера.
Недействительно во время вычисления
Вышеупомянутое технически называется недействительным во время вычисления, что происходит, когда var() ссылается на переменную CSS с ее начальным значением или при использовании допустимой переменной CSS с недопустимым значением свойства CSS. Рассмотрим следующий пример.
1 2 3 4 |
.section-title { top: 10px; top: clamp(5px, var(--offset), 20px); } |
Если браузер не поддерживает функцию clamp(), будет ли работать как запасной вариант top: 10px? Короткий ответ: нет. Причина в том, что к тому времени, когда браузер обнаружил недопустимое значение свойства, он уже ввел другие значения каскада. Это означает, что он проигнорирует top: 10px.
Согласно спецификации CSS: Концепция недопустимого при вычислении значения существует потому, что переменные не могут «выйти из строя раньше», как это могут делать другие синтаксические ошибки, поэтому к тому времени, когда пользовательский агент понимает, что значение свойства недействительно, он уже отбрасывает другие каскадные значения.
В результате, если вы хотите использовать функцию CSS, которая не поддерживается широко, и в ней есть переменная CSS, вам необходимо использовать CSS @supports. Вот как Леа Веру использовала эту технику в своей статье:
1 2 3 4 5 |
@supports (top: max(1em, 1px)) { #toc { top: max(0em, 11rem - var(--scrolltop) * 1px); } } |
Интересные варианты
Значения URL
У вас может не быть контроля над всеми ресурсами на веб-странице, и некоторые из них должны размещаться в Интернете. В этом случае вы можете сохранить значение URL-адреса ссылки в переменной CSS.
1 2 3 4 5 6 7 |
:root { --main-bg: url("https://example.com/cool-image.jpg"); } .section { background: var(--main-bg); } |
Но вы можете задаться вопросом, можно ли интерполировать переменные CSS с помощью url(). Учтите следующее:
1 2 3 4 5 6 7 |
:root { --main-bg: "https://example.com/cool-image.jpg"; } .section { background: url(var(--main-bg)); } |
Это невозможно, так как var(—main-bg) рассматривается как url само по себе, что недопустимо. К тому времени, когда браузер вычислит значение, оно уже недействительно и не будет работать должным образом.
Хранение нескольких значений
Что действительно полезно, так это то, что вы можете хранить несколько значений независимо от значения переменной. Если они действительны, значит, все должно работать. Рассмотрим следующий пример:
1 2 3 4 5 6 7 |
:root { --main-color: 35, 90, 209; } .section-title { color: rgba(var(--main-color), 0.75); } |
В этом примере у нас есть функция rgba(), а значения RGB хранятся в переменной CSS, разделенные запятой. Это может обеспечить гибкость, если вы захотите настроить альфа-значение в зависимости от элемента.
Единственным недостатком этого является то, что невозможно изменить значение rgba с помощью средства выбора цвета DevTools. Если это важно для вашего варианта использования или проекта, возможно, вам придется пересмотреть вариант использования rgba, как описано выше.
Другой пример — использование свойства background.
1 2 3 4 5 6 7 8 9 10 11 |
:root { --bg: linear-gradient(#000, #000) center/50px; } .section { background: var(--bg); } .section--unique { background: var(--bg) no-repeat; } |
У нас есть два раздела, и для одного из них требуется, чтобы фон не повторялся по осям x и y.
Анимация переменных внутри правила @keyframes
Если вы читали спецификацию переменных CSS, вы могли встретить термин animation-tainted. Идея состоит в том, что при использовании переменной CSS внутри правила @keyframes ее нельзя анимировать.
1 |
<div class="box"></div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
.box { width: 50px; height: 50px; background: #222; --offset: 0; transform: translateX(var(--offset)); animation: moveBox 1s infinite alternate; } @keyframes moveBox { 0% { --offset: 0; } 50% { --offset: 50px; } 100% { --offset: 100px; } } |
Анимация не будет работать плавно. Будет анимироваться поле только для значений (0, 50px, 100px). Согласно спецификации CSS: … любое пользовательское свойство, используемое в правиле @keyframes, становится искаженным анимацией, что влияет на то, как оно обрабатывается при обращении к нему через функцию var() в свойстве анимации.
Если мы хотим, чтобы приведенная выше анимация работала, мы должны сделать это по старинке. Это означает, что нам нужно заменить переменную фактическим свойством CSS, которое мы хотим анимировать.
1 2 3 4 5 6 7 8 9 10 11 |
@keyframes moveBox { 0% { transform: translateX(0); } 50% { transform: translateX(50px); } 100% { transform: translateX(100px); } } |
Дэнни Винтер указал, что можно анимировать переменные CSS внутри ключевых кадров, зарегистрировав их с помощью @property. На данный момент это поддерживается в браузерах Chromium.
1 2 3 4 5 |
@property --offset { syntax: "<length-percentage>"; inherits: true; initial-value: 0px; } |
Вычисления
Возможно, вы не знали, что можете выполнять вычисления с переменными CSS. Рассмотрим следующий пример, который я объяснил ранее.
1 2 3 4 5 |
.c-avatar { display: inline-block; width: calc(var(--size, 1) * 30px); height: calc(var(--size, 1) * 30px); } |
У нас могут быть вариации аватара. Я установил значение по умолчанию 1, поэтому размер по умолчанию (30 пикселей * 30 пикселей). Обратите внимание на различные варианты классов и на то, как изменение значения —size приводит к изменению размера аватара.
1 2 3 4 5 6 7 8 9 10 11 |
.c-avatar--small { --size: 2; } .c-avatar--medium { --size: 3; } .c-avatar--large { --size: 4; } |
Инструменты разработчика и переменные CSS
Есть несколько полезных приемов, которые мы можем использовать в DevTools браузера, чтобы упростить работу с переменными CSS. Давайте изучим их!
Возможность увидеть значение цвета
Разве не полезно видеть визуальный индикатор значения цвета или фона, когда вы используете переменную CSS? Chrome и Edge показывают это.
Вычисленные значения
Чтобы увидеть вычисленное значение переменной CSS, вам нужно навести указатель мыши или кликнуть мышью в зависимости от браузера.
Все вычисленные значения могут отображаться при наведении курсора, за исключением Safari, вам нужно нажать на кнопку с двумя линиями.
Автозаполнение для переменных CSS
В большом проекте сложно запомнить все имена переменных. Но все в порядке! Вы можете ввести — и несколько символов, и получите список с переменными CSS на странице. Это работает в Chrome, Firefox и Edge.
Отключение переменной CSS
Когда вам нужно отключить переменную CSS для всех элементов, которые ее используют, это можно сделать, сняв флажок с элемента, для которого она определена. См. рисунок ниже:
Заключение
Было много сказано о переменных CSS. Теперь я счастлив, что у меня наконец-то есть для них отдельная страница. Я надеюсь, что вы нашли это полезным, и если да, пожалуйста, поделитесь информацией. Спасибо за чтение!
Автор: Ahmad Shadeed
Источник: ishadeed.com
Редакция: Команда webformyself.