Для майже останнього допису у цьому році я обрав тему, про яку збирався розповісти дуже давно. І от, 9 грудня цього року, непомітно для усіх, сталася моя особиста подія року у веброзробці.
Firefox нарешті додав підтримку @scope.
Проблема ізоляції селекторів в CSS стара як світ. В першу чергу через те, що механізм каскаду не передбачає ізоляції, лише перевизначення. Специфічність, порядок,
!important врешті решт.Люди всіляко намагалися це вирішити, кидаючись з крайнощі в крайнощі — то богопротивний БЕМ, то дияволоугодний CSS-in-JS.
Аж ось, 2021 року, зʼявилася цікава специфікація —
@scope. Вона окреслювала механізм "області видимості" стилів, який давав можливість дійсно ізолювати селектори. Перша реалізація не забарилася, і вже 2023 року підтримка зʼявилася у Chrome/Edge, з наступного року у Safari, ну а до Firefox її нам підвезли майже під ялиночку.Чому це важливо? По факту, механізм
@scope працює не на ізоляцію селекторів, а на їхню область видимості. За замовчуванням усі селектори в CSS глобальні. А от @scope дозволяє сказати, що .title всередині .card поводиться ось таким чином. І якщо ми визначаємо певне "обмежене" правило, то стилі будуть застосовуватися виключно до елемента всередині області видимості. А поза нею до такого ж селектора — не будуть.Працює воно, з однієї сторони, просто, з іншої — не дуже. Давайте поглянемо на простий приклад:
<div class="card">
<h2 class="title">Превіт</h2>
</div>
<h2 class="title">Світ</h2>
@scope (.card) {
.title { color: lime }
}
.title { color: red }.title всередині .card буде зеленого кольору, ззовні — червоного. Як бачите, навіть якщо ми маємо перевантаження стилів нижче у файлі, усе одно будуть застосовані відповідні кольори.Логічне питання: може це тому, що
@scope додає специфічности? Але відповідь — ні, не додає. Ви можете перевірити це в Dev Tools.Хоча зі специфічністю таки є нюанс — якщо я замість просто
.title напишу h2.title в останньому правилі поза @scope, то цей стиль таки перебʼє наш зелений колір. "Специфічність, безсердечна ти сука", як сказав би Шелдон Купер.Можна, звичайно ж, написати звично:
.card .title { … }Але. В такий спосіб зʼявиться додаткова специфічність, якої зі
@scope немає. Специфічність селектора в @scope визначається самим селектором, а не специфічністю селектора, яким задається область видимості. Тому навіть така конструкція матиме специфічність (0,1,0):@scope (#card.override) {
.title { color: lime }
}На відміну від
#card.override .title, яка матиме специфічність (1,2,0).А ще області видимості можна задавати явно і початок, і кінець:
@scope (.parent) to (.child) { ... }Це значить, що область видимості діє всередині
.parent, але не поширюється на піддерево, коренем якого є .child.Я особисто використовую
@scope для стилізації своїх компонентів. Це дозволяє уникати складного "ізоляційного" коду, а також використовувати різні "утилітарні" класи без обмазування їх специфічністю.Таким чином я можу спокійно мати щось на кшталт:
<h2 class="size-xxl"></h2>
<div class="card size-xxl"></div>
і описувати ось цей
size-xxl всередині відповідного скоупа в один простий селектор замість бавитися у "Вгадай специфічність":@scope (:is(h1, h2, h3)) {
.size-xxl { … }
}
@scope (.card) {
.size-xxl { … }
}Способів застосування є безліч насправді. І якщо грамотно побудувати домовленості в команді, то просте використання
@scope дозволить вам без особливих проблем писати дійсно ізольовані стилі без використання різного ступеню кучерявости костилів.@scope в цілому дозволяє писати чистіший та зрозуміліший CSS, а також явно керувати правилами застосування селекторів. Це дуже важливий момент — не специфічністю, а саме тим, чи буде селектор застосовано взагалі. Це дуже суттєва різниця, яку необхідно дуже чітко зрозуміти.Що почитати:
MDN: @scope
Що почитати душнілам:
W3C: Scoping Styles: the @scope rule
Вогник, серденько, цьом у лобіка.
@babichdev