CSS Variables или CSS Custom Properties уже давно используются в разработке и поддерживаются большинством популярных браузеров. Если у вас нет обязательного требования разрабатывать под IE, то вполне вероятно, вы уже успели оценить их преимущества.

По этой теме написано множество статей, но я сфокусируюсь на том, чтобы показать распространенные кейсы по использованию, которые сам применял на практике. Будет мало теории, но много кода.

Варианты использования

1. Определение переменных

Начнём с базовых вещей, а именно: как определять переменные. Стоит помнить, что их можно определить как в корневом элементе :root (переменные будут доступны по всему приложению), так и внутри любого селектора (будут доступны только для выбранного элемента и его дочерних элементов):

:root {
    /* Определение переменной внутри корневого элемента */
    --color: green;
}
body {
    /* Определение переменной внутри селектора */
    --font: 15px;
    background-color: var(--color);
    font-size: var(--font);
}

2. Переопределение переменных

В примере показан кейс, когда при наведении на элемент, должен меняться цвет. Для этого мы переопределяем переменную --color по событию :hover:

body {
    --color: green;
}
button {
    color: var(--color);
}
button:hover {
    --color: red;
}

Второй пример показывает переопределение переменной в @media выражении. К сожалению, мы не можем вынести 1024px в переменную и использовать ее в @media - это является одним из ограничений CSS переменных.

:root {
    --color: green;
}
body {
    background-color: var(--color);
}
@media (max-width: 1024px) {
    :root {
        --color: red;
    }
}

3. Локальный fallback

Представьте, что вы вызываете переменную --color-green, а ее не существует. Страшного не случится, но заданное CCS-свойство не отработает. Для подстраховки можно задать резервное значение вторым аргументом. Это значение также может быть CSS-переменной:

:root {
    --color-green: green;
    --color-blue: blue;
    --color-red: red;
}
body {
    color: var(--color-green, blue);
    color: var(--color-green, var(--color-blue));
    color: var(--color-green, var(--color-blue, var(--color-red, red)));
}

4. Привязка переменных

При объявлении переменных могут использоваться другие переменные:

:root {
    --color1: var(--color2);
    --color2: var(--color3);
    --color3: red;
}
body {
    background-color: var(--color1);
}

5. Переменные в calc()

В calc() можно перемножать числа со значениями в единицах измерения, н-р: px.

На выходе получим результирующее значение в той единице измерения, на которую умножили.

В примере показано, как перемножается 2 на 10px и в итоге получается 20px. Не важно, используем мы для этого обычные значения или CSS-переменные - результат будет одинаковый:

:root {
    --increment: 2;
    --size: 10px;
}
body {
    /* Будет 20px; */
    font-size: calc(var(--increment) * var(--size));
}

К примеру, у нас есть переменная --font: 20. Но без указания единицы измерения мы не сможем ее корректно использовать в font-size.

Это можно решить с помощью calc(). Для преобразования числа в px, к примеру, достаточно умножить число на 1px в calc():

:root {
    --font: 20;
}
div {
    /* Будет 20px; */
    font-size: calc(var(--font) * 1px);
}

6. Прозрачность в цветовых функциях

Задания цвета - самый распространенный кейс использования переменных.

Вот стандартный пример по использованию hex-цвета для определения значения переменной:

:root {
    /* hex формат */
    --color-blue: #42c8f5;
}
body {
    color: var(--color-blue);
}

Часто бывает, что для цвета нужно задавать различную прозрачность. В CSS для этого есть:

  • rgba()

  • hsla()

  • hwb()

  • #rrggbbaa hex color notation

Использование rgba()

При использовании переменных, удобнее это делать функцией rgba(), которая принимает 4 числовых значения для:

  1. Красного цвета

  2. Зеленого цвета

  3. Синего цвета

  4. Альфа-канала для задания прозрачности

На самом деле, внутри CSS допускается использовать практически всё (за небольшим исключением), даже код на JavaScript!

Поэтому, задание в переменной значения цветов для Red, Green, Blue - вполне допустимо.

:root {
    /* Указание значений цветов: Red Green Blue */
    --color-blue--rgb: 66, 200, 245;
}

Вызовем функцию rgba() с переменной --color-blue--rgb. Для rgba() не хватает четвертого аргумента задающего прозрачность - добавим его через запятую:

body {
    background-color: rgba(var(--color-blue--rgb), 0.7);
}

На выходе собираются аргументы для rgba(): значения из переменной и альфа-канала.

По итогу получаем цвет:

body {
    background-color: rgba(66, 200, 245, 0.7);
}

Использование hsla()

Кроме rgba() можно использовать и hsla(). Это не так удобно, но как вариант, можно попробовать.

Идея следующая:

  1. Определяем переменные с базовыми значениями для основных параметров hsla(): --hue--saturation--lightness--opacity.

  2. При использовании, указываем все базовые параметры в селекторе.

  3. Меняем один / несколько переменных в селекторе (изменения коснутся только данного селектора и его дочерних элементов).

:root {
    --hue: 285;
    --saturation: 100%;
    --lightness: 60%;
    --opacity: 0.7;
}
body {
    /* Переопределяем значение для --hue */
    --hue: 400;

    background-color: hsla(
        var(--hue),
        var(--saturation),
        var(--lightness),
        var(--opacity)
    );
}

На выходе получаем цвет:

body {
    background-color: hsla(400, 100%, 60%, 0.7);
}

7. Переменные в SVG

С помощь переменных мы также можем изменять цвет внутри SVG: fill или stroke. Сложность заключается в том, что изображение SVG должно быть инлайново встроено на страницу, но при использовании сборщиков - это не проблема.

Итак, имеем SVG-элемент на странице, у которого в fill указана переменная --color:

<svg class="icon">
    <circle cx="50" cy="50" r="50" fill="var(--color)" />
</svg>

И саму переменную --color в CSS:

:root {
    --color: green;
}

Значение переменной можно переопределять при необходимых условиях: например, при событии hover на SVG:

.icon:hover {
    --color: pink;
}

Использование с JavaScript. API CSS Style Declaration

CSS переменные работают в runtime, в отличие переменных препроцессоров. Это значит, что мы можем получить к ним доступ в процессе работы приложения через JavaScript.

Рассмотрим, как можно работать с CSS переменными через JavaScript:

В CSS у нас есть 2 переменные:

:root {
    --color: green;
}

А вот код на JavaScript:

// Получаем корневой элемент в DOM
const root = document.querySelector(':root');

// Получаем "Вычисленные свойства"
const rootStyles = getComputedStyle(root);

// Получаем значение переменной по ее названию
const color = rootStyles.getPropertyValue('--color'); // => 'green'

// Изменяем значение переменной
root.style.setProperty('--color', '#88d8b0');

// Удаляем CSS переменную
root.style.removeProperty('--color');

Для примера, я показал все возможные действия с переменными: чтение, изменение, удаление. Использовать операции можно по необходимости.

Вычисленные свойства

В коде выше я затронул тему "Вычисленных свойств". Рассмотрим подробнее: для этого создадим небольшой пример:

HTML-код:

<h1>Hello</h1>

CSS-код:

h1 {
    font-size: 5rem;
}

Единицы измерения можно подразделить на абсолютные и относительные:

  • Абсолютные - это пиксели (px), которые привязываются к разрешению экрана.

  • Относительные (н-р: rem) формируются относительно заданного параметра.

Для отображения, относительные единицы измерения должны быть преобразованы в абсолютные. Если мы откроем инспектор объектов в Google Chrome (или другом браузере) на вкладке "Elements", то сможем увидеть это преобразование:

  • В секции Styles - значения в том виде, в котором мы их указали в CSS (относительные).

  • В секции Computed - значения, приведенные к абсолютным единицам измерения. Функцией getComputedStyle в JavaScript мы как раз таки и получаем эти вычисленные значения.

Так для чего же может понадобиться работать с CSS-переменными через JavaScript? Самый распространенный кейс - это задание цветовой схемы: при переключении схемы мы обращаемся к CSS-переменной и просто изменяем ее значение. Все значения, где используется эта переменная, будут обновлены.

Небольшую демку по изменению цветовой схемы можно посмотреть здесь.

Проверка поддержки переменных

Из CSS:

@supports ( (--a: 0) ) {
    /* Стили с поддержкой переменных */
}
@supports ( not (--a: 0) ) {
    /* Стили без поддержки переменных */
}

Из JavaScript:

if (window.CSS && window.CSS.supports && window.CSS.supports('--a', 0)) {
    // Сценарии с поддержкой переменных
} else {
    // Сценарии без поддержки переменных
}

Достоинства и ограничения

Достоинства:

  • Работают в runtime, в отличие от переменных препроцессоров

  • Можно обратиться из JavaScript

Нельзя использовать:

  • В именах обычных свойств CSS: var(--side): 10px

  • В значениях media-запросов: @media screen and (min-width: var(--desktop))

  • В подстановке URL: url(var(--image))

Ограничения:

  • Нельзя сбрасывать значения всех переменных --: initial

Заключение

Использование CSS-переменных еще один шаг к отказу от препроцессоров. Ждем реализации миксинов @apply на CSS и CSS Nesting Module!

Комментарии (8)


  1. SergeiMinaev
    16.11.2021 00:30

    Возможно, глупый вопрос, но если селекторы :root и html идентичны (да, :root выше по приоритету, но в веб-страничках ведь всё лежит в html), то почему во всех мануалах css-переменные объявляют не в html {}, а именно в :root {} ? Есть этому какое-то нормальное объяснение? Логика же абсолютно такая же, как с цветом текста или размером шрифта, но их никто не указывает в :root, обычно их указывают в html или body и не выпендриваются :) Когда-то давно, когда я впервые с этим столкнулся, то меня это сбило с толку, и я подумал, что переменные можно объявлять исключительно в :root.


    1. beDenz
      18.11.2021 21:31
      +2

      Просто кроме html есть ещё xml и svg. А :root это корень любого типа документа.


  1. i360u
    16.11.2021 07:53
    +2

    Если поддерживается @supports, поддерживаются и переменные. Смысла в такой проверке нет.


  1. Valerdos_UA
    16.11.2021 09:03

    Интересная статья, спасибо.


  1. AlanRow
    16.11.2021 09:03

    Любопытно, не знал, что переменные можно использовать в SVG. Вот только пример с цветом кнопки во втором пункте вызвал вопросы - разве при этом цвет не изменится везде, где он использовался, а не только на кнопке. Понятно, что это модельный пример, но кажется, что как раз в таких случаях лучше воздержаться от переопределения переменной и просто переназначить цвет.


    1. anton-sergeenkov Автор
      16.11.2021 09:46

      "разве при этом цвет не изменится везде, где он использовался" нет, не изменится. Переопределяется локальная переменная внутри button:hover. На работу других (не дочерних блоков) никак не повлияет.


  1. mustaqeem
    16.11.2021 09:37

    Здорово. А что в последних версиях IE переменные ещё не поддерживаются? (я не проверял, не знаю, не пользуюсь IE)


    1. anton-sergeenkov Автор
      16.11.2021 09:38

      Поддержка IE уже завершилась, но еще не протяжении нескольких лет на некоторых проектах, придётся писать под него. Сейчас Edge основной браузер Microsoft