Використання кастомних властивостей (змінних) CSS

Кастомні властивості (іноді звані змінними CSS чи каскадними змінними) – сутності, визначені авторами коду на CSS, що представляють певні значення для перевикористання в документі. Вони задаються за допомогою директиви @property або синтаксису кастомних властивостей (наприклад, --primary-color: blue;). Вони доступні за допомогою функції CSS var() (наприклад, color: var(--primary-color);).

Складні вебсайти мають дуже багато коду на CSS, і це часто призводить до великої кількості повторюваних значень CSS. Наприклад, нерідко можна зустріти один і той же колір, що використовується в сотнях різних місць у списках стилів. Зміна кольору, який було дубльовано в багатьох місцях, потребує пошуку та заміни в усіх правилах і файлах CSS. Кастомні властивості дають змогу визначити значення в одному місці, а потім використовувати його в багатьох інших місцях, щоб з ним було легше працювати. Іншою перевагою є читабельність та семантика. Наприклад, --main-text-color легше зрозуміти, ніж шістнадцятковий колір #00ff00, особливо якщо він використовується в різних контекстах.

Кастомні властивості, визначені за допомогою двох дефісів (--), підлягають каскадності та успадковують значення від свого предка. Директива @property дає змогу краще керувати кастомною властивістю та дозволяє вказати, чи успадковує вона значення від предка, яке її початкове значення, а також обмеження типів, які повинні застосовуватися.

[!NOTE] Змінні не працюють всередині медіазапитів та контейнерних запитів. Функцію var() можна використовувати в будь-якій частині значень будь-яких властивостей елемента. Не можна використовувати var() замість назв властивостей, селекторів і будь-чого іншого, крім значень властивостей, тобто її не можна вживати в медіазапитах і контейнерних запитах.

Оголошення кастомних властивостей

У CSS оголосити кастомну властивість можна за допомогою префікса в вигляді двох дефісів перед іменем властивості або за допомогою директиви @property. У наступних розділах описано, як використовувати ці два методи.

Використання префікса з двох дефісів (--)

Кастомна властивість з префіксом двох дефісів починається з --, після чого стоїть назва властивості (наприклад, --my-property), а потім значення властивості, яке може бути будь-яким дійсним значенням CSS. Як і будь-які інші властивості, кастомні властивості записуються всередині наборів правил. Наступний приклад демонструє те, як створити кастомну властивість --main-bg-color і використати значення <named-color> brown:

section {
  --main-bg-color: brown;
}

Селектор, якому задано набір правил (у прикладі вище – елементи <section>) визначає область, у якій можна використовувати кастомну властивість. У зв'язку з цим поширеною практикою є визначення кастомних властивостей на псевдокласі :root, щоб їх можна було використовувати глобально:

:root {
  --main-bg-color: brown;
}

Це не обов'язково: можуть бути підстави для обмеження сфери дії кастомних властивостей.

[!NOTE] Імена кастомних властивостей є чутливими до регістру: --my-color вважатиметься окремою кастомною властивістю поруч із --My-color.

Використання директиви @property

Директива @property дає змогу визначати кастомні властивості з більшою виразністю, маючи змогу пов'язувати її з типом, задавати усталені значення та контролювати успадкування. Наступний приклад створює кастомну властивість --logo-color, яка очікує значення <color>:

@property --logo-color {
  syntax: "<color>";
  inherits: false;
  initial-value: #c0ffee;
}

Для того, щоб визначати чи працювати з кастомними властивостями в JavaScript, а не безпосередньо в CSS, є відповідний API. Прочитати про те, як він працює, можна на сторінці API властивостей і значень CSS.

Звертання до кастомних властивостей за допомогою var()

Незалежно від того, в який спосіб визначено кастомну властивість, використовувати її можна, звертаючись до неї в функції var() замість стандартного значення властивості:

details {
  background-color: var(--main-bg-color);
}

Перші кроки із кастомними властивостями

Почнімо з HTML, до якого застосуємо трохи стилів. Є <div>, що виступає контейнером, який вміщає трохи дочірніх елементів, частина з яких мають вкладені елементи:

<div class="container">
  <div class="one">
    <p>Один</p>
  </div>
  <div class="two">
    <p>Два</p>
    <div class="three">
      <p>Три</p>
    </div>
  </div>
  <input class="four" placeholder="Чотири" />
  <textarea class="five">П'ять</textarea>
</div>

Наступний CSS використовується для оформлення кількох різних елементів залежно від їхніх класів (частина правил компонування нижче не показана, щоб зосередитись на кольорах). Залежно від їхніх класів, елементам задається колір тла cornflowerblue або aquamarine:

/* Кожному класу задати колір */
.one {
  background-color: cornflowerblue;
}
.two {
  color: black;
  background-color: aquamarine;
}
.three {
  background-color: cornflowerblue;
}
.four {
  background-color: cornflowerblue;
}

.five {
  background-color: cornflowerblue;
}

Це виводить наступний результат:

Є можливість використовувати кастомні властивості для заміни повторюваних значень у цих правилах. Коли визначити --main-bg-color в області .container та використати її значення у декількох місцях, оновлені стилі отримають такий вигляд:

/* Визначити --main-bg-color тут */
.container {
  --main-bg-color: cornflowerblue;
}

/* Для кожного з класів – задати колір */
.one {
  background-color: var(--main-bg-color);
}
.two {
  color: black;
  background-color: aquamarine;
}
.three {
  background-color: var(--main-bg-color);
}
.four {
  background-color: var(--main-bg-color);
}
.five {
  background-color: var(--main-bg-color);
}

Використання псевдокласу :root

Для деяких оголошень CSS можна оголосити значення вище за каскадом, щоб успадкування CSS розв'язало цю проблему. Для нетривіальних проєктів це не завжди можливо. Оголошуючи кастомну властивість на псевдокласі :root і використовуючи її в документі, автор CSS коду може зменшити потребу в повторенні:

/* Визначити --main-bg-color тут */
:root {
  --main-bg-color: cornflowerblue;
}

/* Для кожного з класів – задати колір */
.one {
  background-color: var(--main-bg-color);
}

.two {
  color: black;
  background-color: aquamarine;
}
.three {
  background-color: var(--main-bg-color);
}
.four {
  background-color: var(--main-bg-color);
}

.five {
  background-color: var(--main-bg-color);
}

Це веде до такого ж результату, як і в попередньому прикладі, але дає змогу використовувати одне канонічне оголошення значення властивості (--main-bg-color: cornflowerblue;), що дуже корисно, якщо пізніше знадобиться змінити значення в усьому проєкті.

Успадкування кастомних властивостей

Кастомна властивість, визначена за допомогою двох дефісів --, а не @property, завжди успадковує значення від свого предка. Це продемонстровано у наступному прикладі:

<div class="one">
  <p>Один</p>
  <div class="two">
    <p>Два</p>
    <div class="three"><p>Три</p></div>
    <div class="four"><p>Чотири</p></div>
  </div>
</div>
div {
  background-color: var(--box-color);
}

.two {
  --box-color: cornflowerblue;
}

.three {
  --box-color: aquamarine;
}

Результат var(--box-color) залежить від успадкування наступним чином:

  • class="one": недійсне значення, яке є усталеним значенням кастомної властивості, визначеної таким чином
  • class="two": cornflowerblue
  • class="three": aquamarine
  • class="four": cornflowerblue (успадковане від предка)

Одним з аспектів кастомних властивостей, який демонструють приклади вище, є те, що вони не працюють точно так само, як змінні в інших мовах програмування. Значення обчислюється там, де воно потрібне, а не зберігається та використовується в інших місцях списку стилів. Наприклад, не можна задати значення властивості та розраховувати на отримання того самого значення в правилах нащадків сусіднього елемента. Властивість задається лише для відповідного селектора та його нащадків.

Використання @property для керування успадкуванням

Директива @property дає змогу явно вказати, успадковує властивість значення, чи ні. Наступний приклад створює кастомну властивість за допомогою директиви @property. Успадкування вимкнено, визначено тип даних <color> та усталене значення cornflowerblue.

Батьківський елемент задає --box-color зі значенням green і використовує --box-color як значення свого кольору тла. Дочірній елемент також використовує background-color: var(--box-color), і ми очікуємо, що він матиме колір green, якщо успадкування ввімкнено (або якщо воно визначено за допомогою синтаксису з двома дефісами).

<div class="parent">
  <p>Батьківський елемент</p>
  <div class="child">
    <p>Дочірній елемент з вимкненим для --box-color успадкуванням.</p>
  </div>
</div>
@property --box-color {
  syntax: "<color>";
  inherits: false;
  initial-value: cornflowerblue;
}

.parent {
  --box-color: green;
  background-color: var(--box-color);
}

.child {
  width: 80%;
  height: 40%;
  background-color: var(--box-color);
}

Оскільки в директиві задано inherits: false;, а значення для властивості --box-color не визначено всередині області .child, використовується усталене значення cornflowerblue, а не те, що було б успадковано від батьківського елемента – green:

Запасні значення кастомних властивостей

Задати запасні значення для кастомних властивостей можна за допомогою функції var() та налаштування initial-value директиви @property.

[!NOTE] Запасні значення не використовуються для виправлення проблем сумісності, коли кастомні властивості CSS не підтримуються, оскільки запасне значення не допоможе в такому випадку. Запасні значення покривають ситуації, коли браузер підтримує кастомні властивості CSS та може використовувати інше значення, якщо бажана змінна ще не визначена або має недійсне значення.

Визначення запасних варіантів у функції var()

Використовуючи функцію var(), можна визначити кілька запасних значень, коли задана змінна ще не визначена; це може бути корисно при роботі з кастомними елементами та тіньовим DOM.

Перший аргумент цієї функції – назва кастомної властивості. Другий аргумент – необов'язкове запасне значення, яке використовується як заміна, коли посилання на кастомну властивість недійсне. Ця функція приймає два параметри, присвоюючи все, що стоїть після першої коми, другому з них. Якщо другий параметр недійсний, запасне значення не спрацює. Наприклад:

.one {
  /* Червоний, якщо --my-var не визначена */
  color: var(--my-var, red);
}

.two {
  /* рожевий, якщо --my-var та --my-background не визначені */
  color: var(--my-var, var(--my-background, pink));
}

.three {
  /* Недійсно: "--my-background, pink" */
  color: var(--my-var, --my-background, pink);
}

Додання кастомної властивості як запасного варіанту, як у другому прикладі вище (var(--my-var, var(--my-background, pink))), є правильним способом надання більш ніж одного запасного варіанту за допомогою var(). Проте слід пам'ятати про вплив цього методу на швидкодію, оскільки обробка вкладених змінних займає більше часу.

[!NOTE] Синтаксис запасного варіанту, як і синтаксис кастомних властивостей, дозволяє використання ком. Наприклад, var(--foo, red, blue) визначає запасний варіант red, blue — усе, що передано між першою комою та кінцем виклику функції, вважається запасним значенням.

Запасні варіанти при використанні початкового значення @property

Крім використання var(), як механізм запасних варіантів можна використовувати налаштування initial-value директиви @property.

Фактично ми це вже бачили в розділі Успадкування з @property.

Наступний приклад за допомогою правила @property задає початкове значення --box-color як cornflowerblue. У наборі правил, що стоїть після директиви, хотілося задати --box-color як aquamarine, але в назві значення є хибодрук. Те саме істинно для третього <div>, де використано 2rem для кастомної властивості, яка очікує дійсне значення <color>. І 2rem, і aqumarine є недійсними значеннями кольору, тож застосовується початкове значення cornflowerblue:

@property --box-color {
  syntax: "<color>";
  initial-value: cornflowerblue;
  inherits: false;
}

.one {
  --box-color: aquamarine;
  background-color: var(--box-color);
}

.two {
  --box-color: aqumarine;
  background-color: var(--box-color);
}

.three {
  --box-color: 2rem;
  background-color: var(--box-color);
}

Недійсні кастомні властивості

Кожній властивості CSS можна присвоїти визначений набір значень. Якщо спробувати присвоїти значення властивості, яке не входить до її набору дійсних значень, воно вважається недійсним.

Коли браузер зустрічає недійсне значення для звичайної властивості CSS (наприклад, значення 16px для властивості color), він відкидає оголошення, а елементи отримують значення, які вони мали б, якби цього оголошення не існувало. У наступному прикладі помітно, що відбувається, коли звичайне оголошення CSS є недійсним; color: 16px; відкидається, і застосовується попереднє правило color: blue:

<p>Цей абзац початково є чорним.</p>
p {
  color: blue;
}

p {
  /* ой, це не дійсний колір */
  color: 16px;
}

Проте коли розбираються значення кастомних властивостей, браузер ще не знає, де вони будуть використовуватися, тож він майже завжди вважає їх дійсними. На жаль, такі дійсні значення можуть бути вжиті, за допомогою функції var(), у контексті, де вони можуть не мати сенсу. Властивості та кастомні змінні можуть призводити до недійсних інструкцій CSS, що призводить до поняття дійсності під час обчислення.

Коли браузер зустрічає недійсну заміну var(), то використовується початкове або успадковане значення властивості. Цей приклад практично такий самий, як попередній, окрім того, що він використовує кастомну властивість.

Браузер на місці замінює var(--text-color) значенням --text-color, але 16px не є дійсним значенням властивості color. Після такої заміни властивість не має сенсу., тож браузер обробляє цю ситуацію у два етапи:

  1. Перевірити, чи властивість кольору можна успадкувати. Так, втім, <p> не має жодного предка із властивістю color. Тому – перехід до наступного кроку.
  2. Встановити значення усталеного початкового значення, тож – чорний.
<p>Цей абзац початково є чорним.</p>
:root {
  --text-color: 16px;
}

p {
  color: blue;
}

p {
  color: var(--text-color);
}

Для таких випадків директива @property може запобігти неочікуваним результатам, дозволяючи визначити початкове значення властивості:

<p>Цей параграф початково є чорним.</p>
@property --text-color {
  syntax: "<color>";
  inherits: false;
  initial-value: cornflowerblue;
}

:root {
  --text-color: 16px;
}

p {
  color: blue;
}

p {
  color: var(--text-color);
}

Значення у JavaScript

Використання значень кастомних властивостей у JavaScript відбувається аналогічно до використання стандартних властивостей.

// отримати змінну із вбудованого стилю
element.style.getPropertyValue("--my-var");

// отримати змінну звідки завгодно
getComputedStyle(element).getPropertyValue("--my-var");

// встановити змінну на вбудованому стилі
element.style.setProperty("--my-var", jsVar + 4);

Дивіться також