Переменные 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 <html> element*/ .dark-mode {   --text-color: #e9e9e9;   --border-color: #434343;   --main-bg-color: #434343;   --action-bg-color: #363636; }

Установка значения по умолчанию


Иногда нужно установить переменную 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 и все. Такой подход помогает, например, в создании прототипов сеток.

<div class="wrapper" style="--item-width: 250px;">   <div></div>   <div></div>   <div></div> </div>

.wrapper {   display: grid;   grid-template-columns: repeat(auto-fill, minmax(var(--item-width), 1fr));   grid-gap: 1rem; }

Демо

Аватары пользователей


Полезный вариант применения — определение размеров элементов. Допустим, нужно четыре разных размера аватара пользователя с возможностью контроля размера с помощью только одной переменной.

<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" />

.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 наследуются. Если в родительском элементе определена переменная, дочерние элементы наследуют ее. Пример:

<div class="parent">   <p class="child"></p> </div>

.parent {   --size: 20px; }  .child {   font-size: var(--size); }

Элемент .child будет иметь доступ к переменной —size в результате наследования ее от своего родителя. Интересно, правда? Как извлечь из этого пользу? Что ж, вот пример из жизни.

У нас есть группа действий с такими требованиями:

  • Возможность изменять размер всех элементов, изменяя только одну переменную.
  • Интервал должен быть динамическим (сжиматься при уменьшении размера элемента и увеличиваться при увеличении размера элемента).

<div class="actions">   <div class="actions__item"></div>   <div class="actions__item"></div>   <div class="actions__item"></div> </div>

.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 она не может работать в анимации.

<div class="box"></div>

.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: "<length-percentage>";   inherits: true;   initial-value: 0px; }

Расчеты


Возможно, вы не знаете, что с переменными CSS можно выполнять вычисления. Рассмотрим следующий пример, который я объяснял ранее.

.c-avatar {   display: inline-block;   width: calc(var(--size, 1) * 30px);   height: calc(var(--size, 1) * 30px); }

У нас могут быть вариации аватара. Я установил значение по умолчанию в 1, поэтому размер по умолчанию равен (30px * 30px). Обратите внимание на различные вариации классов и на то, как изменение значения --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 и подпишитесь на обновления о книге.

image

Читать еще

ссылка на оригинал статьи https://habr.com/ru/company/skillfactory/blog/523130/