Пользовательские свойства CSS
04.05.2017

Пользовательские свойства CSS

Опрос Эшли Нолана в ноябре 2016 года показал, что 84% фронтенд-разработчиков используют CSS-препроцессор. В то же время самой полезной возможностью препроцессоров разработчики считают переменные. Это неудивительно. Переменные помогают упорядочить код, избежать дублирования, улучшают читаемость, делают проще рефакторинг.

У препроцессорных переменных есть ограничения: они ничего не знают о структуре DOM, не работают в псевдо-классах и медиа-запросах, их нельзя прочитать или изменить с помощью JavaScript.

Но главный недостаток препроцессоров — стили нужно компилировать, нельзя запустить код сразу в браузере.

На эту тему рекомендуем посмотреть доклад Вадима Макеева «Мой ванильный CSS»

Пользовательские свойства CSS (CSS custom properties) выполняют функцию нативных переменных в CSS и не имеют указанных ограничений. А с недавних пор они поддерживаются всеми современными браузерами. В этой статье мы научимся объявлять и использовать пользовательские свойства.

Объявление и использование

Объявить пользовательское свойство можно с помощью двойного дефиса (--):


:root {
    --main-color: #ff0000;
}

Вы можете спросить, а почему не @ как в LESS, или $ как в Sass, ведь это гораздо приятнее глазу? Такой синтаксис позволяет использовать нативные переменные вместе с любым препроцессором, и они не будут компилироваться и попадут в неизменном виде в результирующий CSS.

Автор спецификации Таб Аткинс в своем блоге объясняет выбор синтаксиса для пользовательских свойств.

Если мы используем $ foo для переменных, мы не сможем использовать его для будущих «похожих на переменные» вещей.

Имя пользовательского свойства чувствительно к регистру. Свойство --main-color — это не то же самое, что --Main-color.

Значением пользовательского свойства может быть любое допустимое в CSS значение: цвет, строка, ключевое слово, даже выражение.

Допустимо даже такое значение свойства:


--foo: if(x > 5) this.width = 10;
С точки зрения CSS это некорректное значение, но его потенциально можно прочитать и обработать из JavaScript. Это открывает простор для фантазии разработчиков.

Получить значение пользовательского свойства можно с помощью функции var().


.box{
    color: var(--main-color, black);
}

Обратите внимание на второй аргумент функции var(). Его значение выполняет роль фоллбэка и применяется, когда переменная не определена. В качестве фоллбэка может использоваться значение другой переменной:


:root {
    --color-main:;
    --color-default: black;
}

.box {
    color: var(--color-main, var(--color-default));
}

В примере выше мы намеренно не задали значение переменной с помощью выражения --color:;. Это означает, что переменная объявлена, но не определена. Не путайте с --color: ; — здесь значением переменной является пробел, а это некорректное значение.

Значение пользовательского свойства можно использовать для объявления другого свойства.


:root {
    --main-color: #ff0000;
    --border: 2px var(--main-color) solid;
}

.box{
    color: var(--main-color);
    Border: var(--border);
}

Значения пользовательских свойств можно складывать, вычитать, умножать и делить с помощью функции calc().


:root {
    --base-font-size: 14px;

    --font-size-xl: calc(var(--base-font-size) + 10px);
    --font-size-l: calc(var(--base-font-size) + 4px);
}

Область видимости и наследование

Переменные в Sass, как и в других препроцессорах, существуют вне селектора. Такие переменные считаются глобальными. Пользовательские свойства можно объявлять только внутри селектора. Этот селектор ограничивает область видимости свойства. Спецификация рекомендует использовать псевдо-класс :root для объявления глобальных пользовательских свойств.

Пользовательские свойства подчиняются правилам каскада и наследуются до последнего потомка.


:root {
    --color: black;
}

.box-black {
    --color: white;
}

.title {
    color: var(--color);
}
В примере выше мы объявили переменную --color и задали ей значение black. Затем переопределили значение для селектора .box-black. А блок .title просто использует текущее значение переменной, каким бы оно ни было. В результате, в белом блоке заголовок выводится черным цветом, а в черном - белым.

Использование ключевых слов

Пользовательские свойства CSS подчиняются тем же правилам, что и обычные свойства. Это означает, что вы можете назначить им одно из ключевых слов:

  • inherit — применяется значение родительского элемента.
  • initial — применяется начальное значение, определенное в спецификации CSS (пустое значение или ничего для пользовательских свойств CSS).
  • unset — применяется унаследованное значение, если свойство наследуется (как в случае пользовательских свойств) или начальное значение, если свойство не наследуется.
  • revert — сбрасывает свойство к значению по умолчанию, установленному таблицей стилей браузера (пустое значение в случае пользовательских свойств).

Установить для всех свойств начальное значение можно с помощью свойства all:


.box {
    all: initial;
}

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


.box {
    --: initial;
}

Отличия пользовательских свойств от препроцессорных переменных

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

Значения пользовательских свойств подставляются во время выполнения, поэтому могут изменяться «на лету» и использоваться в медиа-запросах.

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

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

Поскольку пользовательские свойства CSS — это такие же свойства, доступ к ним можно получить из JavaScript с помощью API CSSStyleDeclaration.


/**
* Получение значения пользовательского свойства
*/
function readCssVar(element, varName){
    const elementStyles = getComputedStyle(element);
    return elementStyles.getPropertyValue(`${varName}`).trim();
}

/**
* Запись пользовательского свойства
*/
function writeCssVar(element, varName, value){
    return element.style.setProperty(`${varName}`, value);
}

Поддержка

Все современные браузеры уже поддерживают CSS Custom Properties:

Desktop

Chrome Opera Firefox IE Edge Safari
49 36 31 No 15 9.1

Mobile

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
9.3 37 No 56 57 52

Если вам нужна поддержка старых браузеров, можно использовать пользовательские свойства параллельно с препроцессорными, а поддержку проверять с помощью директивы supports:


$color: red;
:root {
    --color: red;
}

.box {
    @supports ( (--a: 0)) {
        color: var(--color);
    }
    @supports ( not (--a: 0)) {
        color: $color;
    }
}

Можно трансформировать код с CSS-переменными, например, с помощью плагина postcss-custom-properties. Но, в отличие от компиляции препроцессором, плагин позволяет сохранить переменные м вызовы функции var() для тех браузеров, которые их поддерживают.

Дополнительные материалы

Дополнительно рекомендуем прочитать спецификацию CSS Custom Properties for Cascading Variables Module Level 1.

Отличные подробные статьи на английском языке:

Также советуем посмотреть доклад Павла Ловцевича "Пользовательские свойства как основа архитектуры CSS" с FrontTalks, где он подробно рассказывает о том, почему пользовательские свойства крутые.

В Firefox с 2014 года, в Chrome и Safari с марта 2016 года, в Edge с апреля 2017 года (Источник: CanIUse)

Подробнее про функцию calc() в справочнике
Подробнее про псевдо-класс :root в справочнике