Ох уж эти CSS-переменные

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.


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

▍ Разрешённые символы при именовании


Однажды я наткнулся примерно на следующий фрагмент кода:


:root {
  --component-font-size: 20px;
}

.component {
  --_component-font-size: var(--component-font-size);
  font-size: var(--_component-font-size, 10px);
}

Для меня нижнее подчёркивание — это прямая отсылка к старому соглашению, которое использовалось в JS для создания «внутренних» свойств объекта. Я удивился, что в CSS так тоже можно!


Как говорится в спецификации CSS Custom Properties for Cascading Variables Module Level 1, название пользовательского свойства — это специальный CSS-идентификатор <dashed-ident>, начинающийся с --. Одновременно к нему применимы правила синтаксиса CSS-идентификатора <ident>, описанного в стандарте CSS Syntax Module Level 3. По правилам мы можем использовать символы [a-zA-Z0-9], дефис -, подчёркивание _ и другие ASCII-символы, если их экранировать.


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


body {
  --\*: lightgoldenrodyellow;
  background-color: var(--\*);
}

Честно говоря, если бы меня спросили на собеседовании, корректный ли этот код, то я бы точно сказал нет. Предыдущий пример взорвал мне мозг, но он не единственный.


▍ Разрешены символы не только латиницы


Однажды я потратил несколько часов, чтобы найти ошибку. Браузер не определял значение для пользовательского свойства. Для объяснения я сократил тот код до следующего:


body {
  --color: red;
  background-color: var(--сolor);
}

Вы думаете, цвет фона у элемента body стал красным? Вот я также думал. А он не был красным. Я стал инспектировать код и увидел, что ошибки нет. Но по какой-то причине значение red не применялось для пользовательского свойства --color. После медитации над кодом я решил просто скопировать --color и вставить его для свойства background-color. Код заработал.


Оказывается, дело в том, что при именовании пользовательских свойств мы можем использовать как латинские символы, так и кириллические. И случайно в строке --color: red я напечатал первую с на английском языке, а в строке background-color: var(--сolor) уже на русском. С точки зрения браузера это два разных пользовательских свойства, и поэтому значение не применялось.


Одно дело, когда перепутал один символ, но следующий код также будет рабочим:


body {
  --цвет-фона: red;
  background-color: var(--цвет-фона);
}

Фон у элемента body будет красный. Честно, я не знаю, почему браузеры могут обрабатывать символы на русском языке. Я пытался нагуглить, почему код работает. Ничего не нашёл. Если вам известен ответ, то, пожалуйста, поделитесь в комментариях.


▍ Некорректное значение не вызывает ошибки


Мы рассмотрели нюансы при именовании пользовательских свойств, но кроме них есть ещё моменты, которые сбивают с толку при работе с ними. В качестве первого примера рассмотрим код, в котором передано некорректное значение 10px через пользовательское свойство --not-a-background-color для свойства background-color. Как думаете, какое итоговое значение будет во вкладке Computed?


:root { 
  --not-a-background-color: 10px; 
}

.container { 
  background-color: lightgoldenrodyellow; 
  background-color: var(--not-a-background-color); 
}

Можно подумать, что lightgoldenrodyellow, но нет. Правильный ответ — transparent.


В стандарте сказано, что если при замене пользовательского свойства получается некорректное значение для свойства, то браузеры вместо него будут использовать корректное. Оно вычисляется в зависимости от свойства. Если оно наследуемое, то значение передаётся в результате наследования. Если нет, используется начальное (initial) значение.


В предыдущем примере после замены var(--not-a-background-color) браузеры определяют, что значение 10px некорректное. Далее они проверяют, можно ли унаследовать значение. Свойство background-color не наследуется, поэтому подставляется начальное значение, т. е. transparent.


:root { 
  --not-a-background-color: 10px; 
}

.container { 
  background-color: lightgoldenrodyellow; 
  background-color: var(--not-a-background-color); /* после замены var(--not-a-background-color) будет background-color: transparent */
}

А если бы у нас было свойство, которое можно наследовать, например font-size, то значение было унаследованное.


:root { 
  --not-a-font-size: lightgoldenrodyellow; 
}

body {
  font-size: 10px;
}

.container { 
  font-size: 20px; 
  font-size: var(--not-a-font-size); /* после замены var(--not-a-font-size) будет font-size: 10px */
}

▍ Краткая форма свойства


При работе с пользовательскими свойствами важно помнить, как они работают внутри свойств, которые являются краткой формой для других свойств, т. е свойства padding, margin, border и т. п. В качестве примера я определил свойство border и передал только два значения:


:root {    
  --border-width: 2px;
  --border-style: solid;
}

.container {
  border: var(--border-width) var(--border-style) var(--border-color);
}

Согласно стандарту, в случае отсутствия хотя бы одного значения браузеры не могут найти все составляющие свойства. В результате они не могут применить определённые нами значения. Так произошло в моём примере. В devTools мы увидим, что значения переданы, но свойство border не применилось.


▍ Пользовательские свойства — это не переменные


Пользовательские свойства часто сравнивают с переменными из любого языка программирования, но это не так! Для примера создам градиент с помощью следующего кода:


:root {
  --color-one: green;
  --color-two: blue;
  --gradient: linear-gradient(to bottom, var(--color-one), var(--color-two));
}

.container {
  background-image: var(--gradient);
}

Браузеры отобразят его от начального цвета green до конечного blue. Но что будет, если для элемента с классом box создать «переменную» --color-one со значением red?


:root {
  --color-one: green;
  --color-two: blue;
  --gradient: linear-gradient(to bottom, var(--color-one), var(--color-two));
}

.container {
  --color-one: red;	
  background-image: var(--gradient);
}

Если бы пользовательские свойства были переменными, как в языках программирования, то градиент стал бы от начального цвета red до конечного blue. Но браузеры не сохраняют значения пользовательских свойств! Они только передают их.


:root {
  --color-one: green;
  --color-two: blue;
  --gradient: linear-gradient(to bottom, var(--color-one), var(--color-two));
}

.container {
  --color-one: red;	
  background-image: var(--gradient); /* здесь будет значение linear-gradient(to bottom, var(--color-one), var(--color-two)) */
}

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


▍ !important не всегда !important


Посмотрите, пожалуйста, на следующий фрагмент кода. Как думаете, каким цветом будет текст?


.container {
  --text-color: red !important;
  color: var(--text-color);
}

.container {
  color: green;
}

Красный? Я тоже так ответил в первый раз, и это неправильный ответ. Браузеры отобразят текст зелёным, и вот почему.


Пользовательские свойства — это полноценные свойства, поэтому если при объявлении значений для них используется !important, то он имеет приоритет только среди пользовательских свойств с таким именем. Таким образом, в примере при объявлении пользовательского свойства --text-color !important создаёт приоритет для него, а не для свойства color. А поскольку второе правило объявлено ниже по коду, то оно будет иметь приоритет по правилам специфичности.


А если мы будем использовать !important не при объявлении значения пользовательского свойства, а для свойства color, то приоритет будет уже у первого правила, поэтому текст уже будет красным.


.container {
  --text-color: red;
  color: var(--text-color) !important;
}

.container {
  color: green;
}

▍ Ключевое слово inherit


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


<body>
  <div class="parent">
    <div class="child">какой-то текст внутри элемента div</div>
  </div>	  
</body>	

.parent {
  --main-font-size: 50px;
  font-size: 30px;
}

.child {
  --main-font-size: inherit;
  font-size: var(--main-font-size, inherit);
}

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


В нашем примере в строке --main-font-size: inherit с помощью ключевого слова inherit произойдёт наследование от пользовательского свойства --main-font-size, а не от свойства font-size.


.parent {
  --main-font-size: 50px;
  font-size: 30px;
}

.child {
  --main-font-size: inherit; /* здесь значение 50px */
  font-size: var(--main-font-size, inherit);
}

После замены функции var() браузеры подставят 50px для свойства font-size, и в итоге получим font-size: 50px для элемента с классом child.


Мы рассмотрели наследование от родительского пользовательского свойства, а что будет, если его не указывать?


.parent {
  font-size: 30px;
}

.child {
  --main-font-size: inherit;
  font-size: var(--main-font-size, inherit);
}

В этом случае браузеры не могут получить значение с помощью ключевого слова inherit из строки --main-font-size: inherit, поэтому они ничего не передадут. В этом случае будет использовано значение по умолчанию inherit, с помощью которого браузеры передадут значение 30px от свойства font-size элемента с классом parent.


.parent {
  font-size: 30px;
}

.child {
  --main-font-size: inherit; /* здесь нет значения */
  font-size: var(--main-font-size, inherit); /* здесь будет font-size: 30px */
}

▍ Вместо заключения


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


Telegram-канал с розыгрышами призов, новостями IT и постами о ретроиграх
Источник: https://habr.com/ru/companies/ruvds/articles/749734/


Интересные статьи

Интересные статьи

Младший научный сотрудник московской лаборатории экспериментальной микробиологии ФГБНУ НИИВС им И.И. Мечникова Ольга Афанасьева согласилась ответить на некоторые вопросы редакции «Хабра», касающиеся е...
Корпоративная (пред)историяРуководство одной  крупной полугосударственной компании, предоставляющей услуги населению, столкнулось с постоянным снижением доходов по нескольким направлениям из-за в...
Вы, наверное, уже знаете о том, что для хранения сведений об отдельных компонентах цвета можно применять пользовательские CSS-переменные. Это позволяет избавиться от необходимости повторе...
CSS-переменные (их ещё называют «пользовательскими свойствами») поддерживаются веб-браузерами уже почти четыре года. Я пользуюсь ими там, где они могут пригодиться. Это зависит от про...
deviantart.com/orioto Предыдущая часть. Видеоверсия внизу. Зеленая тропа Основной упор при разработке был направлен на разнообразие мира. Зеленая Тропа — не просто версия Пе...