CSS-переменные: почему вас это должно волновать?

Переменные CSS, более точно известные как пользовательские свойства CSS, появятся в Chrome 49. Они могут быть полезны для уменьшения повторения в CSS, а также для мощных эффектов во время выполнения, таких как переключение тем и потенциальное расширение/полизаполнение будущих функций CSS.

CSS-беспорядок

При разработке приложения общепринятой практикой является выделение набора фирменных цветов, которые будут повторно использоваться для обеспечения единообразия внешнего вида приложения. К сожалению, повторение этих значений цвета снова и снова в CSS — это не только рутинная работа, но и чревато ошибками. Если в какой-то момент один из цветов необходимо изменить, вы можете отбросить осторожность и «найти и заменить» все вещи, но в достаточно большом проекте это может легко стать опасным.

В последнее время многие разработчики обратились к препроцессорам CSS, таким как SASS или LESS, которые решают эту проблему за счет использования переменных препроцессора. Хотя эти инструменты значительно повысили производительность разработчиков, переменные, которые они используют, имеют один серьезный недостаток: они статичны и не могут быть изменены во время выполнения. Добавление возможности изменять переменные во время выполнения не только открывает двери для таких вещей, как динамическое оформление тем приложений, но также имеет серьезные последствия для адаптивного дизайна и потенциальной возможности полифиллинга будущих функций CSS. С выпуском Chrome 49 эти возможности теперь доступны в виде пользовательских свойств CSS.

Коротко о пользовательских свойствах

Пользовательские свойства добавляют в наш набор инструментов CSS две новые функции:

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

Вот краткий пример, демонстрирующий

:root {
    --main-color: #06c;
}

#foo h1 {
    color: var(--main-color);
}

--main-color — это пользовательское свойство, определенное автором, со значением #06c. Обратите внимание, что все пользовательские свойства начинаются с двух дефисов.

Функция var() извлекает и заменяет значение пользовательского свойства, в результате чего получается color: #06c; Если пользовательское свойство определено где-то в вашей таблице стилей, оно должно быть доступно функции var .

На первый взгляд синтаксис может показаться немного странным. Многие разработчики спрашивают: «Почему бы просто не использовать $foo для имен переменных?» Этот подход был специально выбран так, чтобы быть максимально гибким и потенциально допускать использование макросов $foo в будущем. Предысторию вы можете прочитать в этом посте от одного из авторов спецификации, Таба Аткинса.

Синтаксис пользовательского свойства

Синтаксис пользовательского свойства прост.

--header-color: #06c;

Обратите внимание, что пользовательские свойства чувствительны к регистру, поэтому --header-color и --Header-Color — это разные пользовательские свойства. Хотя на первый взгляд они могут показаться простыми, разрешенный синтаксис для пользовательских свойств на самом деле весьма разрешителен. Например, следующее допустимое пользовательское свойство:

--foo: if(x > 5) this.width = 10;

Хотя это было бы бесполезно в качестве переменной, поскольку было бы недопустимо в любом обычном свойстве, потенциально ее можно было бы прочитать и обработать с помощью JavaScript во время выполнения. Это означает, что пользовательские свойства могут раскрыть все виды интересных методов, которые в настоящее время недоступны с помощью современных препроцессоров CSS. Так что, если вы думаете: « зевать , у меня есть SASS, так что кого это волнует…», тогда взгляните еще раз! Это не те переменные, с которыми вы привыкли работать.

Каскад

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

:root { --color: blue; }
div { --color: green; }
#alert { --color: red; }
* { color: var(--color); }
<p>I inherited blue from the root element!</p>
<div>I got green set directly on me!</div>
<div id="alert">
    While I got red set directly on me!
    <p>I’m red too, because of inheritance!</p>
</div>

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

:root {
    --gutter: 4px;
}

section {
    margin: var(--gutter);
}

@media (min-width: 600px) {
    :root {
    --gutter: 16px;
    }
}

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

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

:root {
    --primary-color: red;
    --logo-text: var(--primary-color);
}

Функция вар()

Чтобы получить и использовать значение пользовательского свойства, вам понадобится функция var() . Синтаксис функции var() выглядит следующим образом:

var(<custom-property-name> [, <declaration-value> ]? )

Где <custom-property-name> — это имя пользовательского свойства, определенного автором, например --foo , а <declaration-value> — это резервное значение, которое будет использоваться, когда указанное настраиваемое свойство недействительно. Резервные значения могут представлять собой список, разделенный запятыми, который будет объединен в одно значение. Например var(--font-stack, "Roboto", "Helvetica"); определяет запасной вариант "Roboto", "Helvetica" . Имейте в виду, что сокращенные значения, например те, которые используются для полей и заполнения, не разделяются запятыми, поэтому подходящий запасной вариант для заполнения будет выглядеть следующим образом.

p {
    padding: var(--pad, 10px 15px 20px);
}

Используя эти резервные значения, автор компонента может написать защитные стили для своего элемента:

/* In the component’s style: */
.component .header {
    color: var(--header-color, blue);
}
.component .text {
    color: var(--text-color, black);
}

/* In the larger application’s style: */
.component {
    --text-color: #080;
    /* header-color isn’t set,
        and so remains blue,
        the fallback value */
}

Этот метод особенно полезен для оформления тем веб-компонентов, использующих Shadow DOM, поскольку пользовательские свойства могут пересекать границы тени. Автор веб-компонента может создать первоначальный дизайн, используя резервные значения, и предоставить «крючки» тем в виде пользовательских свойств.

<!-- In the web component's definition: -->
<x-foo>
    #shadow
    <style>
        p {
        background-color: var(--text-background, blue);
        }
    </style>
    <p>
        This text has a yellow background because the document styled me! Otherwise it
        would be blue.
    </p>
</x-foo>
/* In the larger application's style: */
x-foo {
    --text-background: yellow;
}

При использовании var() есть несколько ошибок, на которые следует обратить внимание. Переменные не могут быть именами свойств. Например:

.foo {
    --side: margin-top;
    var(--side): 20px;
}

Однако это не эквивалентно установке поля margin-top: 20px; . Вместо этого второе объявление недействительно и выбрасывается как ошибка.

Точно так же вы не можете (наивно) создать значение, часть которого предоставляется переменной:

.foo {
    --gap: 20;
    margin-top: var(--gap)px;
}

Опять же, это не эквивалентно настройке поля margin-top: 20px; . Чтобы получить значение, вам понадобится еще кое-что: функция calc() .

Построение значений с помощью Calc()

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

.foo {
    --gap: 20;
    margin-top: calc(var(--gap) * 1px); /* niiiiice */
}

Работа с пользовательскими свойствами в JavaScript

Чтобы получить значение пользовательского свойства во время выполнения, используйте метод getPropertyValue() вычисленного объекта CSSStyleDeclaration.

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>I’m a red paragraph!</p>
/* JS */
var styles = getComputedStyle(document.documentElement);
var value = String(styles.getPropertyValue('--primary-color')).trim();
// value = 'red'

Аналогичным образом, чтобы установить значение пользовательского свойства во время выполнения, используйте метод setProperty() объекта CSSStyleDeclaration .

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>Now I’m a green paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'green');

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

/* CSS */
:root {
    --primary-color: red;
    --secondary-color: blue;
}
<!-- HTML -->
<p>Sweet! I’m a blue paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'var(--secondary-color)');

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

Поддержка браузера

В настоящее время Chrome 49, Firefox 42, Safari 9.1 и iOS Safari 9.3 поддерживают настраиваемые свойства.

Демо

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

Дальнейшее чтение

Если вам интересно узнать больше о пользовательских свойствах, Филип Уолтон из команды Google Analytics написал руководство о том, почему он так заинтересован в пользовательских свойствах , и вы можете следить за их прогрессом в других браузерах на chromestatus.com .