Зачем нужны кастомные свойства и как они работают?

В языках программирования есть переменные: вы что-нибудь один раз объявляете, присваиваете значение, а потом снова и снова используете. Если значение переменной меняется, то оно меняется везде. Похоже на местоимения в языке: из предложения или абзаца всегда понятно, кто «мы» или что за «оно», мы их как будто объявили раньше и наверняка переопределим потом.


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


Как работают препроцессоры? Вы пишете на каком-то языке, который внешне напоминает CSS, а иногда вообще не напоминает.


@mixin sass
  @if &
    &:hover
      color: tomato
  @else
    a
      color: plum

Потом это компилируется в настоящие стили. Переменные там — это такая сложная автозамена переменных на их значения. Sass, Less, Stylus, PostCSS-плагины — все они работают только так. Эти переменные существуют только в разработке, в браузере их уже нет.


.scss {
  $variable: value;
  color: $variable;
}

Если сравнить такое поведение с JavaScript, то становится очень обидно за CSS. В JS переменные живут прямо в браузере, их можно создавать и менять на лету. Тем не менее такие переменные и другие элементы программирования в CSS-препроцессорах получили огромную популярность. Уже почти не бывает больших проектов без препроцессоров.


К счаcтью нашлись люди, недовольные такими куцыми переменными. После ряда черновиков и вариаций синтаксиса, Таб Аткинс написал спецификацию полноценных CSS-переменных, точнее, кастомных свойств. Эти переменные поддерживаются уже в 70% браузеров по миру и сильно меняют принцип написания стилей.


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


Например, у нас сейчас есть свойство box-shadow, чтобы отбрасывать тень. А раньше его не было, оно появилось первым в браузере Safari в 2008 году. Но появилось не просто так, а как -webkit-box-shadow, с префиксом, начинающимся с дефиса. То есть разработчики движка WebKit придумали своё свойство. Только потом, когда оно стало частью стандарта и появилось в других браузерах, префикс убрали.


.shady {
  -webkit-box-shadow: 0 0 0 4px tomato;
  box-shadow: 0 0 0 4px tomato;
}

Теперь вы тоже можете создавать собственные свойства, только не нужно указывать между дефисами название движка: дефис, дефис, название свойства. Давайте создадим свойство --box-shadow-color и зададим ему значение tomato. Чтобы использовать это значение в коде, нужно передать его в функцию var().


.shady {
  --box-shadow-color: tomato;
  box-shadow: 0 0 0 5px var(--box-shadow-color);
}

Мы сейчас объявили новое свойство, которое потом можно повторно использовать снова и снова. А ещё свойства box-shadow-color никогда не было в природе и чтобы менять тени, например, по наведению, приходилось переписывать box-shadow целиком. А теперь по ховеру можно просто поменять значение переменной. Круто?


.shady {
  --box-shadow-color: tomato;
}
.shady:hover {
  --box-shadow-color: plum;
}

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


.shady {
  font-size: var(--font-size, 12px);
}

Из-за того, что это кастомные свойства, а не просто доллары с автозаменой, они ведут себя как обычные свойства: наследуются вглубь для всех детей элемента и не видны между элементами-соседями. Чтобы переменную точно было видно во всём документе, её нужно задать самому корневому элементу <html>, но лучше даже :root, на случай если это корневой элемент <svg>.


:root {
  --font-size: 12px;
  --theme-color: tomato;
}

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


@media (min-width: 320px) {
  .shady {
    --font-size: 48px;
  }
}

Кастомные свойства можно использовать даже внутри функции calc(), которая посчитает результат выражения внутри. Уже не очень похоже на привычный CSS, правда? Стоит ли говорить, что все препроцессоры уже умерли от зависти, глядя на такое.


.shady {
  font-size: calc(var(--font-size) * 2);
}

Ещё кастомные свойства становятся идеальным транспортом для данных между скриптами и стилями. Например, вы можете динамически считать координаты мыши в JS и пробрасывать их в кастомные свойства через setProperty в нужный элемент.


element.style.setProperty('--pointer-left', event.clientX);

Дальше в стилях уже можно решить: использовать их в top и left или transform: translate(). И наоборот: значение свойства можно получить в JS с помощью getPropertyValue.


И это я только кастомные свойства лапкой потрогал, дальше ещё куча интересного, что кардинально меняет работу со стилями. Читайте и смотрите дальше сами: статьи, видео и слайды.


Кастомные свойства — это не border-radius, который либо делает красиво, либо нет. Бросаться всё переделывать на них пока рано, вёрстка может сильно поломаться в браузерах, которые их не поддерживают. Но уже пришло время пробовать и уметь.


Видеоверсия



Вопросы можно задавать здесь.

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


  1. Zenitchik
    22.09.2017 19:12

    И, как обычно, Ишак подкачал...


    1. SelenIT3
      22.09.2017 19:50

      Ишак умер (то, что некоторые отдельные, гм… оригиналы отказываются это признавать и закапывать его — другой вопрос). Он больше не поддерживается и не развивается, новых версий не будет. А его наследник, который поддерживается и автоматически обновляется, с октября уже будет поддерживать в полном объеме.


      1. Zenitchik
        22.09.2017 20:05

        Т.е. типичное физлицо не станет просматривать сайт с помощью IE? Все перешли на его наследника или другие браузеры?


        с октября уже будет поддерживать

        Это определённо радует.


      1. dom1n1k
        22.09.2017 21:50
        +1

        IE11 сейчас повторяет роль, которую некогда играл IE6.
        Когда все говорили, что его время ушло, что рыночная доля составляет уже единицы процентов, что он вот-вот должен исчезнуть, что пора прекращать поддержку, что ну вот ещё совсем чуть-чуть и тогда уже точно всё!..
        И так несколько лет подряд.


        1. Zenitchik
          22.09.2017 22:10
          +1

          Вот в том и оно… А ещё есть корпоративный сектор, где IE9, зараза, я уж не знаю какую долю процента составляет, а всё никак за горизонт не уйдёт.


          1. SelenIT3
            24.09.2017 16:58

            Может быть, всё-таки IE8 (последний доступный на XP)? IE9 мог бы уцелеть на висте и семерке, где автообновление отключили году в 2011-м, но разве эти ОС столько живут без переустановки?


            1. Zenitchik
              24.09.2017 19:35

              IE8 — тоже, но меньше.


              ОС столько живут без переустановки

              ОС не живут без прямых рук. А вот без переустановки — запросто. Даже знаменитый глючностью Me.


  1. Varim
    23.09.2017 11:19

    Спасибо, не знал об их существовании.


  1. vtvz_ru
    23.09.2017 13:49

    То есть все переменные в глобальном скоупе?


    1. SelenIT3
      23.09.2017 20:03

      Нет. Переменные видны только в элементе, для которого заданы, и его потомках. Глобальные только те, что заданы для :root.


    1. vtvz_ru
      24.09.2017 11:30

      Кстати говоря, новый дизайн YouTube использует переменные. Случайно заметил



  1. Finesse
    24.09.2017 05:25

    Есть ли полифил для собственных CSS-свойств, работающий в браузере?