Martin Michálek Martin Michálek  – 24. 11. 2022

Kaskádové vrstvy jsou novinka, která webařům umožňuje měnit platnost deklarací bez nutnosti uvádět je na konkrétní místo v CSS souboru a v konkrétním pořadí.

Obecně vzato, CSS Cascade Layers poskytují strukturovaný způsob uspořádání stylů. Slouží tak ke zjednodušení práce s kaskádou v CSS a hlavně specificitou selektorů.

Dejme si jednoduchý příklad:

@layer framework {
  /* Styly pro framework… */
}

@layer override {
  /* Styly pro pravidla, která framework přepisují… */ 
}

/* Přidáme styly do vrstvy frameworku: */
@layer framework {
  .btn {
    color: red;
  }
}

Vývojářky a vývojáři mohou díky pravidlu @layer vytvářet vrstvy pro reprezentaci výchozích nastavení prvků, knihoven třetích stran, motivů vzhledu nebo komponent.

Není přitom nutné přizpůsobovat tomu selektory v rámci jednotlivých vrstev nebo se spoléhat na pořadí zdrojů při řešení konfliktů.

Zároveň nám kaskádové vrstvy poskytují možnost přeskupovat pořadí platnosti deklarací:

@layer base, framework, override;

@layer framework {
  /* Styly pro framework… */
}

@layer base {
  /* Styly pro základnu… */ 
}

Podpora této části specifikace CSS Cascade Layers ve všech moderních prohlížečích je plná. To hlavně díky tomu, že prohlížeče aktuálně velmi dobře spolupracují.

V textu vám ukážu několik příkladů, jak to pro vás může být užitečné. Nejprve vám ale zkusím vysvětlit, proč si myslím, že kaskádové vrstvy jsou skoro až revolučně užitečné.

Věčný boj s kaskádou

Už od vzniku CSS před více než více než čtvrt stoletím se uživatelé CSS dělí do dvou skupin.

Menší skupina (z velké části kodérů a frontend designérů), která CSS miluje i se všemi nedostatky. Ve druhé skupině sedí drtivá většina běžných vývojářů, kteří CSS používají, protože nemají jinou možnost. Kaskádové styly tyto vývojáře více či méně štvou a principy návrhu CSS obvykle nechápou.

Dělicí linkou mezi těmito skupinami je často chápání kaskády v CSS a jejich vlastností – dědičnosti, důležitosti (!important) a hlavně specificity selektorů.

Myslím, že příchod kaskádových vrstev je dobrá zpráva pro tu první skupinu. A skvělá zpráva pro tu druhou.

Obvyklý vývoj a hlavně úpravy CSS probíhají v první skupině vývojářek a vývojářů podle metodik, z nichž je pro tyto účely nejdůležitější ITCSS.

V druhé skupině se často prostě přidávají deklarace na konec souboru a doufá se, že to pomůže:

/* Deklarace má vysokou specificitu selektoru… */
#content .btn {
  color: black;
}

/* … proto přidání této deklarace nepřetíží tu původní… */
.btn { 
  color: red;
}

/* … a tak si autoři CSS často pomohou důležitostí…  */
.btn {
  color: red !important;
}

Poznáváte svůj kód? (Klídek, já takového napsal taky hodně.) Jenže klíčové slovo !important a důležitost v CSS je určena pro jiné účely, a tudíž tohle je trošku prasárna. No, možná i více než trošku…

Řešení s kaskádovými vrstvami je jednoduché. Prostě si jednotlivé části CSS izolujeme do relativně nezávislých vrstev:

/* V základu máme sice vysokou specificitu selektoru… */
@layer base {
  #content .btn {
    color: black;
  }
}

/* … ale v další vrstvě ji můžeme klidně přepsat nízkou specificitou: */
@layer override {
  .btn {
    color: red;
  }
}

Ano, je to přesně tak, kaskáda platí jen uvnitř konkrétních kaskádových vrstev. Rozhoduje jen pořadí vrstev, tak, jak jsou uvedené v CSS nebo jak jej specifikujete na začátku CSS souboru pomocí pravidla @layer.

Jeden původ, jeden origin

Je dobré akcentovat, že kaskádové vrstvy organizují styly v rámci jednoho původu (originu). V CSS jich může být více – prohlížečové styly, autorské styly (ty naše), uživatelské styly.

Tzn. pomocí @layer nelze například z autorských stylů „vstoupit“ do prohlížečového originu.

Příklad s Bootstrapem: bez vrstev

V dalším textu vám Cascade Layers představím krok za krokem pomocí jednoduchého příkladu s přestylováním Bootstrapu.

Začneme příkladem bez vrstev. Framework nejprve importujeme, pak přestylujeme:

@import url("bootstrap.min.css");

body {
  margin: 2rem;
  background: #f2f0ec;
}

.btn {
  background: #abab9d;
}

Na první pohled nám to přestylování takto půjde dobře. V prohlížeči dostaneme, co jsme chtěli, tedy přebarvené <body> a tlačítko:

Problém však nastává při najetí myši na tlačítko (pseudotřída :hover). Barva nezůstává taková, jakou jsme nastavili. Důvodem je vyšší specificita původního selektoru:

/* Styly Bootstrapu - specificita 0,2,0 */
.btn:hover {
  background-color: var(--bs-btn-hover-bg);
}

/* Naše styly - specificita 0,1,0  */
.btn {
  background: #abab9d;
}

Původní selektor s vyšší specificitou tedy vyhrává a naše změna barvy se neaplikuje.

Co s tím? Jak už jsem psal, můžeme to přetížit selektorem stejné nebo vyšší specificity. Můžeme přidat důležitost pomocí !important.

Nebo jinak a lépe – můžeme použít kaskádové vrstvy.

Příklad s Bootstrapem: vrstvy vrství

V další ukázce už jsem použil Cascade Layers a trošku zkomplikoval ty přebíjené vlastnosti:

/* Do vrstvy "bootstrap" importujeme Bootstrap: */

@import url("bootstrap.min.css") layer(bootstrap);

/* Cascade Layers: */

@layer my-styles {
  .btn {
    background: #abab9d;
    border-color: #2e2c08;
  }
}

@layer my-base {
  body {
    margin: 2rem;
    background-color: #f2f0ec;
  }

  .btn {
    border-color: red;
  }
}

Jak je vidět, kromě změny barvy pozadí ještě obarvuji rámečky tlačítek na červeno. Najednou to celé funguje. Výše uvedené problémy nevidíme. Specificita selektorů Bootstrapu nás už netrápí, protože se aplikuje jen uvnitř vrstev.

Příklad s Bootstrapem: změna pořadí vrstev

Můžeme to celé ještě vylepšit. Pravidlo @layer totiž využíváme buď pro vložení deklarací do vrstvy nebo deklaraci pořadí vrstev. To druhé vypadá takto:

@layer bootstrap, my-base, my-styles;

Deklaraci zbylého kódu necháme v původním pořadí, ale vzhled stránky se změní. Tlačítka nebudou mít červenou barvu rámečku, protože vrstva my-base je přepsaná vrstvou my-styles.

Prostě lusknete prsty a všechny bolehlavy s nutností dodržovat pořadí v kódu a hlídat specificitu jsou pryč.

Dávám lajk. Proč vrstvy neexistovaly už před patnácti lety…?!

Odbočka k !important

Než budeme pokračovat, je potřeba udělat odbočku k další části kaskády. Tou je důležitost pravidel a klíčové slovo !important.

Důležitost totiž kromě zvýšení váhy konkrétní deklarace také obrátí pořadí priority. To je velmi důležité.

Stejně to funguje také u kaskádových vrstev. Deklarace, kde použijete !important, budou měnit pořadí.

Příklad s Bootstrapem: použijeme !important

Do našeho příkladu s frameworkem Bootstrap nyní přidáme důležitost:

@layer bootstrap, my-base, my-styles;

@import url("bootstrap.min.css")
layer(bootstrap);

@layer my-styles {
  .btn {
    background: #abab9d !important;
  }
}

@layer my-base {
  body {
    margin: 2rem;
    background-color: #f2f0ec;
  }

  .btn {
    background: #8c4615 !important;
  }
}

Vzhledem k pořadí deklarace kódu byste mohli čekat, že tlačítka budou šedivá (#abab9d), ale ony jsou hnědá (#8c4615).

Důvodem je obrácené skládání vrstev v případě použití !important. Deklarace z vrstvy my-base prostě dostane přednost, protože je níže než vrstva my-styles.

Abych to ještě doplnil o poslední dílek skládačky, v pořadí hrají roli i deklarace neuvedené ve vrstvách:

  1. Nejníže deklarace z jednotlivých vrstev podle pořadí, které jste jim nastavili.
  2. Uprostřed jsou deklarace nezařazené do vrstev.
  3. Nejvýše pak platí deklarace s !important, ovšem v opačném pořadí než jsou uvedené vrstvy.

Je to docela galimatyáš, že?

Ale má to dobré důvody. Nejlépe to uvidíte na obrázku:

Připravil jsem k tomuto ještě tři CodePeny. Jsou o praktickém využití nebo spíše trablech přebíjení !important, které nám nadělil Bootstrap.

V kódu frameworku najdete tyto řádky:

[hidden] {
  display: none !important;
}

Nechme teď stranou důvody, proč bychom to měli chtít přebíjet ve svém kódu. Řekněme, že opravdu moc chceme. V takovém případě nám ovšem vložení pravidla pro přebití do vrstev nepomůže (CodePen). Musíme přebíjející pravidlo ponechat mimo vrstvy (CodePen).

Alternativně pak můžeme vrstvy použít, ale vložit přebíjející pravidlo ještě pod styly Bootstrapu:

@import my-styles, bootstrap;

@import url("bootstrap.min.css") layer(bootstrap);

@layer my-styles {
  [hidden] {
    display: block !important;
  }
}

Vnořování vrstev

Alespoň stručně ještě zmíním možnost vnořování vrstev, který rozebírají na skvělém MDN:

@layer framework {
  @layer layout { 
    /* Vznikla nám vrstva framework.layout */ 
  }
}

Podpora v prohlížečích

Prohlížeče jsou na tom skvěle, díky za optání. Chrome podporuje @layer od verze 99, Safari od verze 15.4 a Firefox od verze 97. Ke dni psaní tohoto textu, což je listopad 2022, je tedy podpora dostupná ve všech moderních prohlížečích. Více je jako vždy na caniuse.com/css-cascade-layers.

Závěr (…budou vrstvy revolucí v organizaci CSS?)

Vrstvy jsou skvělý nástroj, který nám umožní vytvářet kaskádové styly, které budou přehlednější a také snadněji udržovatelné.

Jsou navržené tak, aby mohly způsobit revoluci v organizaci CSS. Zároveň je možné je použít tak, aby se vůbec nezměnila naše běžná práce.

Taky je možné, že se běžná práce kodérů vůbec nezmění a @layer se stane něčím, co se používá jen ve výjimečných případech. Rozdíly mezi teorií a praxí jsou totiž v oblasti organizace CSS velmi časté.

Tak či tak – mám z přítomnosti vrstev v CSS velkou radost a věřím, že vám pomohou, stejně tak jako vám doufám pomohl i tento článek.

1 komentář

Tůma Radoslav Tůma Radoslav

Zkoušel jsem podle tohoto článku přestylovat Bootstrap, ale nezdařilo se, viz http://kod.djpw.cz/zvkd. Vítězí třída Bootstrapu .table > :not(caption) > > {

Zajímavé je, když nalinkuji starší verzi Bootstrapu 5.2.2, chová se to tak, jak potřebuji.

Novinka
Nyní je možné přidávat komentáře.
Pro přidání názoru se prosím
přihlaste nebo si zřiďte účet.