[Перевод] Переменные CSS — курс молодого бойца
Переменные CSS (также известные как настраиваемые свойства) поддерживаются браузерами почти 4 года. Мне нравится применять их в зависимости от проекта или ситуации. Они очень полезны и просты в использовании, но зачастую фронтенд разработчик неправильно использует или неправильно понимает их.
Основная цель статьи: я хочу иметь место, где лежит все, что я знаю о переменных CSS, чтобы узнать и понять больше. Здесь вы прочитаете обо всем, что должны знать о переменных CSS, включая примеры применения в коде. Вы готовы? Тогда погружаемся.
Введение
Переменные CSS — это значения, определенные в документе CSS с целью переиспользования и уменьшения количества избыточных значений CSS. Простой пример:
.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):
: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 чувствительны к регистру.
/* 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, например:
let element = "cool";
function cool() {
let otherElement = "Not cool";
console.log(element);
}
Переменная element
глобальная, поэтому доступна внутри функции cool()
. Однако переменная otherElement
доступна только в функции cool()
. Применим этот принцип к переменным CSS.
:root {
--primary-color: #235ad1;
}
.section-title {
--primary-color: d12374;
color: var(--primary-color);
}
Переменная --primary-color
глобальна и может быть доступна из любого элемента документа. Переопределяя ее в рамках блока объявления .section-title
, ее новое значение работает только там. Визуальный пример показывает лучше:
Мы имеем --primary-color
, используемую для цвета заголовка раздела. Мы можем настроить цвет для избранных авторов и последнего раздела статьи, когда переопределяем их. То же применимо к переменной --unit
. Вот CSS для рисунка выше:
/* 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. Посмотрите на пример:
.section-title {
color: var(--primary-color, #222);
}
Обратите внимание: функция var()
имеет несколько значений. Второй вариант #222
работает только тогда, когда переменная --primary-color
по какой-то причине не определена. Мало того, мы можем вложить var()
в другую var()
.
.section-title {
color: var(--primary-color, var(--black, #222));
}
Эта особенность полезна, когда значение зависит от определенного действия. Резервное значение важно предоставить, когда у переменной нет значения.
Примеры использования
Управление размером компонента
Обычное дело — иметь несколько размеров кнопки в дизайн-системе: маленький, средний и большой. Размеры проще описать с помощью переменной CSS.
.button {
--unit: 1rem;
padding: var(--unit);
}
.button--small {
--unit: 0.5rem;
}
.button--large {
--unit: 1.5rem;
}
Изменяя значение --unit
внутри области видимости компонента кнопки мы создаем разные варианты кнопки.
Переменные CSS и цвета HSL
HSL — английская аббревиатура: оттенок, насыщенность, яркость.
: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.
Если хочется узнать больше о цветах в CSS, я написал о них подробную статью .
Пропорциональное изменение размера
Если вы работали с Photoshop, Sketch, Figma или Adobe XD, то у вас может возникнуть идея удерживать клавишу Shift
для пропорционального изменения размера элемента. В CSS нельзя сделать такое напрямую, но есть обходной путь с применением переменных CSS.
Предположим, есть иконка, которая должна быть квадратной. Я определил переменную --size
и использовал ее как для ширины, так и для высоты.
.icon {
--size: 22px;
width: var(--size);
height: var(--size);
}
Вот оно! Теперь вы можете имитировать эффект Shift, изменяя только значение переменной --size
. Вы можете узнать больше об этом здесь.
CSS Grid
Переменные CSS могут быть крайне полезны для сеток.
.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
.
.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.
:root {
--primary-gradient: linear-gradient(150deg, #235ad1, #23d1a8);
}
.element {
background-image: var(--primary-gradient);
}
Или сохранить одно значение. Возьмем, к примеру, угол наклона:
.element {
--angle: 150deg;
background-image: linear-gradient(var(--angle), #235ad1, #23d1a8);
}
.element.inverted {
--angle: -150deg;
}
Положение фона
Возможно включить значения в переменную. Это полезно, когда элемент позиционируется в зависимости от контекста.
.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 мы можем хранить две их версии и переключаться между ними в зависимости от предпочтений пользователя или системных настроек.
:root {
--text-color: #434343;
--border-color: #d2d2d2;
--main-bg-color: #fff;
--action-bg-color: #f9f7f7;
}
/* A class added to the element*/
.dark-mode {
--text-color: #e9e9e9;
--border-color: #434343;
--main-bg-color: #434343;
--action-bg-color: #363636;
}
Your browser does not support HTML5 video.
Установка значения по умолчанию
Иногда нужно установить переменную CSS с помощью JavaScript. Например, нужно получить высоту расширяемого компонента. Я узнал об этой технике из статьи Майкла Шарнагла.
Переменная --details-height-open
пуста. Она добавляется к определенному элементу HTML. Он будет содержать пиксельное значение. Когда по какой-то причине Javascript не работает, важно указать правильное значение по умолчанию или резервное значение.
.section.is-active {
max-height: var(--details-height-open, auto);
}
Значение auto
— это резервное значение на случай сбоя JavaScript и отсутствия переменной CSS --details-height-open
.
Управление шириной враппера
Враппер веб-сайта может иметь несколько вариантов. Может быть, вам нужна небольшая обертка для страницы, но большая для другой. В таком случае полезны переменные CSS.
.wrapper {
--size: 1140px;
max-width: var(--size);
}
.wrapper--small {
--size: 800px;
}
Встроенные стили
Переменные CSS со встроенными стилями открывают множество возможностей, о которых вы, возможно, не знали. Об этом я написал статью, но здесь я упомяну некоторые интересные варианты применения. Возможно, такой подход не идеален для реально работающих веб-сайтов, но он может быть полезен при создании прототипов и в тестировании различных идей.
Элементы динамической сетки
Добавляем переменную --item-width
внутри атрибута style и все. Такой подход помогает, например, в создании прототипов сеток.
.wrapper {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(var(--item-width), 1fr));
grid-gap: 1rem;
}
Демо
Аватары пользователей
Полезный вариант применения — определение размеров элементов. Допустим, нужно четыре разных размера аватара пользователя с возможностью контроля размера с помощью только одной переменной.
.c-avatar {
display: inline-block;
width: calc(var(--size, 1) * 30px);
height: calc(var(--size, 1) * 30px);
}
Проанализируем CSS выше:
- Во-первых, у нас есть
var(--size, 1)
. Я добавил резервное значение на случай, если оно не добавлено в атрибут style. - Минимальный размер аватара — 30×30 пикселей.
Медиа-запросы
Комбинирование переменных CSS и медиа-запросов очень полезно в настройке переменных, применяемых на всем веб-сайте. Самый простой пример, о котором я думаю, — изменение значения отступов.
:root {
--gutter: 8px;
}
@media (min-width: 800px) {
:root {
--gutter: 16px;
}
}
Любой элемент с --gutter
, изменит отступ в зависимости от размера области просмотра. Разве это не здорово?
Наследование
Да, переменные CSS наследуются. Если в родительском элементе определена переменная, дочерние элементы наследуют ее. Пример:
.parent {
--size: 20px;
}
.child {
font-size: var(--size);
}
Элемент .child
будет иметь доступ к переменной --size в результате наследования ее от своего родителя. Интересно, правда? Как извлечь из этого пользу? Что ж, вот пример из жизни.
У нас есть группа действий с такими требованиями:
- Возможность изменять размер всех элементов, изменяя только одну переменную.
- Интервал должен быть динамическим (сжиматься при уменьшении размера элемента и увеличиваться при увеличении размера элемента).
.actions {
--size: 50px;
display: flex;
gap: calc(var(--size) / 5);
}
.actions--m {
--size: 70px;
}
.actions__item {
width: var(--size);
height: var(--size);
}
Обратите внимание, как --size используется для свойства зазора flexbox
. Это означает, что интервал может быть динамическим и будет зависеть от переменной --size
.
Другой полезный пример — наследование переменных CSS для настройки анимации. Ниже вы видите пример из этой статьи Сандрины Перейры в блоге CSS Tricks.
@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 дважды. Значения наследуются из .walk
и .run
.
Как работает валидация переменных CSS
Когда переменная CSS внутри функции var () невалидна, браузер заменяет ее начальным или унаследованным значением в зависимости от свойства.
:root {
--main-color: 16px;
}
.section-title {
color: var(--main-color);
}
Я использовал 16 пикселей — это значение свойства цвета. Значение неправильное. Поскольку свойство цвета наследуются, вот, что сделает браузер:
- Свойство наследуемо?
- Если да, есть ли у родителя это свойство?
- Да: значение наследуется
- Нет: устанавливается по умолчанию
- Если нет, устанавливается начальное значение
Диаграмма показывает работу браузера:
Некорректное вычисленное значение
Вышеупомянутое технически называется некорректное вычисленное значение. Это происходит, когда var()
ссылается на переменную CSS ее начальным значением или использует допустимую переменную CSS с недопустимым значением свойства.
Посмотрим на пример, о котором я узнал из этой статьи пользователя Lea Verou.
.section-title {
top: 10px;
top: clamp(5px, var(--offset), 20px);
}
Когда браузер не поддерживает функцию clamp()
, будет ли top: 10px
работать как резервное значение. Короткий ответ — нет. Причина в том, что когда браузер обнаружил невалидное значение, он уже отбросил другие значения. Это означает, что top: 10px
будет игнорироваться
Согласно спецификации CSS:
Концепция некорректного вычисленного значения существует потому, что переменные не могут «не работать сразу», как в случае синтаксических ошибок, поэтому к тому времени, когда агент пользователя понимает, что значение свойства недопустимо, он уже отбрасывает другие каскадные значения.
То есть когда вы хотите использовать не распространенную функцию CSS и в ней есть переменная CSS, работайте с @supports
. Вот как Леа Веру использовала эту технику в своей статье:
@supports (top: max(1em, 1px)) {
#toc {
top: max(0em, 11rem — var(--scrolltop) * 1px);
}
}
Интересные находки
URL как значение
Возможно, вы не можете контролировать все ресурсы на веб-странице. Некоторые из них должны размещаться в интернете. В этом случае вы можете сохранить URL-адрес ссылки в переменной CSS.
:root {
--main-bg: url(«https://example.com/cool-image.jpg»);
}
.section {
background: var(--main-bg);
}
Можно задаться вопросом: можно ли интерполировать CSS-переменные с помощью url (). Посмотрим на пример:
:root {
--main-bg: «https://example.com/cool-image.jpg»;
}
.section {
background: url(var(--main-bg));
}
Это невозможно: var(--main-bg)
рассматривается как сам URL, который недопустим. К тому моменту, когда браузер вычислит это значение, оно уже не будет действительным и не будет работать, как ожидалось.
Хранение нескольких значений
Полезно то, что возможно хранить несколько значений независимо от значения переменной. Если они невалидны, то это должно сработать. Посмотрим на пример:
:root {
--main-color: 35, 90, 209;
}
.section-title {
color: rgba(var(--main-color), 0.75);
}
В этом примере у нас есть функция rgba()
. Значения RGB хранятся в переменной CSS, разделенной запятой. Это может обеспечить гибкость в случае, когда вы хотите настроить прозрачность в зависимости от элемента.
Единственный недостаток — невозможно настроить значение rgba
с помощью палитры цветов DevTools. Если это важно для вашего варианта применения или проекта, то вам, возможно, придется пересмотреть использование rgba
, как описано выше.
Другой пример — применение этого свойства со свойством background.
:root {
--bg: linear-gradient(#000, #000) center/50px;
}
.section {
background: var(--bg);
}
.section--unique {
background: var(--bg) no-repeat;
}
У нас есть два раздела. Один из них требует, чтобы фон не повторялся по осям X и Y.
Анимация переменных внутри правила @keyframes
Если вы читали спецификацию для переменных, вы могли прочитать термин animation-tainted — испорченная анимация. Идея в том, что при использовании CSS-переменной внутри правила @keyframes
она не может работать в анимации.
.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.
@keyframes moveBox {
0% {
transform: translateX(0);
}
50% {
transform: translateX(50px);
}
100% {
transform: translateX(100px);
}
}
Обновление: 9 октября 2020 года
Данни Винтер указал, что можно анимировать CSS-переменные внутри ключевых кадров, регистрируя их с помощью @property
. Сейчас это поддерживается в браузерах Chromium.
@property --offset {
syntax: "";
inherits: true;
initial-value: 0px;
}
Расчеты
Возможно, вы не знаете, что с переменными CSS можно выполнять вычисления. Рассмотрим следующий пример, который я объяснял ранее.
.c-avatar {
display: inline-block;
width: calc(var(--size, 1) * 30px);
height: calc(var(--size, 1) * 30px);
}
У нас могут быть вариации аватара. Я установил значение по умолчанию в 1, поэтому размер по умолчанию равен (30 px * 30 px). Обратите внимание на различные вариации классов и на то, как изменение значения --size
приводит к изменению размера аватара.
.c-avatar--small {
--size: 2;
}
.c-avatar--medium {
--size: 3;
}
.c-avatar--large {
--size: 4;
}
В DevTools есть несколько полезных трюков, чтобы облегчить нам работу с переменными CSS. Давайте исследуем их!
Возможность увидеть значение цвета
Разве не полезно видеть визуальный индикатор для значения цвета или фона, когда вы используете переменную CSS? Chrome и Edge показывают цвет.
Вычисленные значения
Чтобы увидеть вычисленное значение, наведите указатель мыши или кликните — в зависимости от браузера.
Все вычисленные значения отображаются при наведении курсора. За исключением Safari, где для этого нужно нажать кнопку с двумя линиями.
Автозавершение переменных CSS
В большом проекте трудно запомнить все имена переменных. Но можно ввести --
, какие-то символы и получить список с переменными CSS на странице. Это работает в Chrome, Firefox и Edge.
Отключение переменной
Когда вам нужно отключить переменную CSS от всех использующих ее элементов, это можно сделать, сняв флажок с элемента, для которого она определена. Смотрите рисунок ниже:
Заключение
Я счастлив, что у меня наконец-то есть для них отдельная страница. Я надеюсь, что вы нашли это полезным, и если да, пожалуйста, поделитесь информацией. Спасибо за чтение!
Я пишу электронную книгу
Рад сообщить вам, что пишу электронную книгу об отладке CSS.
Если вам интересно, перейдите на debuggingcss.com и подпишитесь на обновления о книге.