A CSS @scope nem CSS Modules: a közelebbi komponens felülírhatja a későbbi szabályt

A CSS @scope nem izolál teljesen, viszont a kaszkádba beemeli a DOM-beli közelséget. Ezért egy belső komponens szabálya ugyanazzal a specifikációval is nyerhet.

A komponens CSS-e akkor kezd igazán rossz kedvű lenni, amikor egy kártya belsejében megjelenik egy másik kártya. A külső téma szabálya ráfolyik a belsőre, valaki feltekeri a specifikációt, aztán valaki más !important-ot tesz rá. Így készülnek azok a stylesheet-ek, amelyekhez már a szerzőjük is csak lámpával mer nyúlni.

Egymásba ágyazott CSS scope-ok sematikus ábrája, amelyen a belső scope közelebb van a cél elemhez

A CSS @scope erre ad natív eszközt. Nem CSS Modules, és nem Shadow DOM: a szabályai egy kijelölt DOM-részfában érvényesek. A különös rész nem maga a határ, hanem hogy az @scope egy új döntési pontot ad a kaszkádhoz: két egyébként döntetlen deklaráció közül a cél elemhez közelebbi scope nyer.

A később írt szabály itt veszíthet

Vegyünk egy egymásba ágyazott világos–sötét–világos témát. A kód szándékosan rövid: nem egy dizájnrendszert építünk, csak a kaszkádot piszkáljuk meg.

<div class="theme theme--light">
  <p>Külső világos</p>
  <div class="theme theme--dark">
    <p>Középső sötét</p>
    <div class="theme theme--light">
      <p>Belső világos</p>
    </div>
  </div>
</div>
@scope (.theme--light) {
  :scope { background: #eee; }
  p { color: #171717; }
}

@scope (.theme--dark) {
  :scope { background: #222; }
  p { color: #f8f8f8; }
}

A belső bekezdésre mindkét p szabály illeszkedik, és a sötét blokk ráadásul később áll a fájlban. Régi reflexből fehér szöveget várnánk. Ehelyett sötétet kap: a belső .theme--light scope root egy ugrásra van tőle, a sötét kettőre. A scope proximity a specifikáció döntési sorrendjében a source order előtt áll. Na, ezt nem tudtam, amikor először megláttam az @scope-ot.

Ez nem mindenek felett álló varázserő. A !important, a cascade layer és a specifikáció előbb számít; a közelség csak ezek után, döntetlenben bontja fel a versenyt. Épp ezért lehet vele komponenshatáron belül olvasható, alacsony specifikációjú CSS-t írni anélkül, hogy a fájlok betöltési sorrendére fogadnánk egy kávét.

A root nem ad ingyen specifikációt

Az @scope (#app) belsejében írt button nem válik ID-szintű monstrummá. A scope root szelektora nem adódik hozzá a belső szabály specifikációjához; a button továbbra is egy elem-szelektor. A specifikáció ezt lényegében :where(:scope)-ként kezeli. Ez fontos különbség a kézzel írt #app button-hoz képest, amelyet később már csak még nagyobb kalapáccsal lehet felülírni.

Ha magát a scope rootot akarod formázni, a :scope erre való — a példában ezért kap háttérszínt. Csakhogy ez valódi pseudo-class, tehát már hozzáad egy classnyi specifikációt. A specifikáció ezt a különbséget külön is rögzíti; nem nyelvtani díszlet, hanem későbbi override-oknál látható viselkedés.

A „fánk” közepe nem a tied

Az opcionális to (...) alsó határt jelöl. Az alábbi szabály egy cikk törzsében formázza a képeket, de a beágyazott figure és minden gyereke kimarad:

@scope (.article-body) to (figure) {
  img { border-radius: .5rem; }
}

A felső határ inkluzív, az alsó exkluzív: a figure maga sem része a scope-nak. Emiatt hívják ezt „donut scope”-nak. Hasznos, ha egy CMS-ből jövő rich textet kell rendben tartani, de a beágyazott widget saját belsejéhez nem szabad hozzányúlni. Ettől még nincs teljes izoláció: az örökölhető tulajdonságok, például a color vagy a font-family, a határon túl is öröklődhetnek. Az @scope szelektorhatár, nem miniatűr Shadow DOM.

Mikor vinném productionbe?

Új, modern böngészőkre célzó alkalmazásban igen: különösen egymásba ágyazható komponenseknél, témáknál és CMS-tartalomnál. A Chrome 118-ban már megjelent a funkció, a Safari 17.4 pedig szintén támogatást kapott; az Chrome és a WebKit bejelentései jól mutatják, hogy ez nem egy kísérleti DevTools-trükk. A teljes, több böngészős Baseline elérhetőségét az MDN 2025-től jelöli.

Régi böngészőket is kötelező kiszolgáló publikus felületen viszont nem építenék rá kizárólagos layoutlogikát: az ismeretlen at-rule a benne lévő szabályokkal együtt kiesik. Ilyenkor legyen értelmes, globális alapstílus, és az @scope csak a modern réteg. Nem kell holnap az összes CSS Modules-t kidobni az ablakon. De ahol ma már a specificity-számháború a napi üzem része, ott az @scope meglepően kis eszközzel ad vissza egy normális szabályt: a belső komponens legyen erősebb, mert tényleg belül van.

Források

Leave a Reply

Az e-mail címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük