Kaskádové vrstvy

Narazili jste někdy na problém, že jste museli zvýšit specificitu CSS selektoru, abyste přepsali nějaké pravidlo z frameworku nebo vlastního vzhledu (theme)?

Nejste jediný, a proto W3C přišlo v roce 2021 s návrhem kaskádových vrstem (Cascade Layers) a většina předních vývojářů (Chromium, Webkit i Mozilla) již pracují na co nejrychlejší implementaci.

Co je to vrstvení CSS?

Do roku 2021 bylo CSS organizováno podle priorit a pořadí – tedy CSS pravidlo mohlo přepsat jen jiná pravidla, která jsou definována menším počtem selektorů (nebo selektorů s nižší prioritou) a nebo pravidla se stejnou prioritou načtená dříve.

V tom důsledku bylo nemožné (nebo velmi obtížné nebo alespoň obtěžující) přepisovat inline nebo komponentní vzhledy pomocí frameworků a themů načtených na začátku souboru (v HTML hlavičce).

Návrh vrstvení přidává k tomuto nové pravidlo @layer, které umožňuje nadefinovat do které vrstvy CSS pravidla patří a tudíž s jakou prioritou se mají používat.

Priorita vrstev

Pravidlo @layer má dvě použití: zaprvé pomocí něj definujete jaké vrstvy budete používat a v jakém pořadí je má prohlížeč používat (tzn. priorita). Doporučené vrstvy a pořadí jsou:

@layer reset, base, default, framework, component, theme;

V tomto příkladu bude mít vrstva reset nejnižší prioritu (bude tedy aplikována jako první a ostatní vrstvy ji budou moci přepsat) zatímco vrstva theme bude mít nejvyšší prioritu a bude přepisovat pravidla všech ostatních vrstev.

Jména si můžete samozřejmě vybrat jaká chcete. Pokud prioritu neurčíte, nebo použijete vrstvu, která prioritu určenou nemá, bude priorita určena tak jako doposud – tedy podle pořadí definice vrstev v CSS souborech (nebo inline HTML).

Pravidla rozdělená do vrstev

Druhé použití pravidla @layer je vlastní definice pravidel vrstvy (uvedením složených závorek bloku). Jedna vrstva se může libovolněkrát opakovat a vrstvy se mohou dokonce vnořovat (buď přes tečku nebo vnořením bloků):

@layer reset { 
/* nastavení základní barvy */
    html {
        color: white;
        background: black;
    }
}

@layer theme { 
/* nastavení barvy vzhledu */
    html {
        color: light-gray;
        background: dark-red
    }
}

@layer framework.base { 
/* barva pro vzhled frameworku */
    html {
        color: black;
    }
 }

@layer framework.theme { 
/* barva pro vzhled frameworku */
    html {
        color: light-blue;
    }
 }

@layer reset { 
/* další pravidla do vrstvy s nejnižší prioritou */
    html {
        padding: 0;
    }
}
@layer component { 
/* barva pro vzhled komponent */
    @layer theme { 
    /* = vrstva "component.theme" */
        html {
            color: black;
            background: white;
        }
    }
}

Pokud použijeme priority z příkladu výše, budou barvy přiřazeny v pořadí: reset, framework.base, framework.theme, theme a component.theme.

Také lze určit vnořené priority:

@layer reset, base, framework, component;

@layer component {
    @layer base, theme;
}
@layer framework {
    @layer base, theme, component;
}

V tomto příkladu je pořadá vrstev: reset, base, framework.base, framework.theme, framework.component, component.base a component.theme.

Import do vrstev

Samozřejmě není potřeba všechna pravidla psát do jednoho souboru a rozdělení do souborů záleží na vás. Dokonce ani není potřeba upravovat již existující rozdělení CSS souborů a vrstvu stačí určit při načtení souboru:

@import url(bootstrap.css) layer(framework);
@import url(bs.green.css) layer(framework.theme);
@import url(red.css) layer(theme);

@import url(form.css) layer(component.base);
@import url(bootstrap-form.css) layer(component.framework.theme);
@import url(button.css) layer(component.base);

Prioritu vrstev lze určit i kombinací @layer a @import:

@layer reset { ... }
@import url(framework.css) layer(framework);
@layer base { ... }
@import url(yellow.css) layer(theme);

V tomto příkladu bude priorita vrstev: reset, framework, base a theme.

Obdobně by měl fungovat i import CSS souborů přímo v HTML, ale tento atribut ještě nebyl nadefinován (jelikož HTML spravuje jiný tým než CSS) a tudíž je jeho syntaxe jen odhad:

<!-- POZOR: toto zatím není součástí HTML5 specifikace!!! -->
<link rel="stylesheet" 
        href="framework.css" 
        layer="framework.base"
>
<link rel="stylesheet" 
         href="framework-theme.css" 
         layer="framework.theme"
>

Anonymní vrstvy

Použití pravidla @layer bez jména nebo použití @import bez parametru layer() vytvoří nově tzv. anonymní vrstvu. Ta se chová jako ostatní vrstvy – tedy má prioritu podle toho, kde je v souboru uvedena, s tím rozdílem, že jelikož nemá jméno, není možné do ní později přidávat další pravidla. Použití několika @layer bez jména nebo @import bez layer() vytvoří různé anynonymní vrstvy, které jsou na sobě nezávislé:

@layer reset { ... } /* pojmenovaná vrstva */
@layer { ... } /* anonymní vrstva 1 */
@import url(framework.css); /* anonymní vrstva 2 */
@layer { ... } /* anonymní vrstva 3 */
@import url(theme.css); /* anonymní vrstva 4 */

Hlavní vrstva

Pravidla, která jsou zapsána přímo do CSS nebo HTML bez určení vrstvy budou zahrnuta to tzv. výchozí (default) vrstvy (pozor: neplést s tím, že vámy vytvořená vrstva se také může jmenovat default a jde o jinou vrstvu než tu výchozí). Hlavní vrstva má nejvyšší prioritu a bude tedy přepisovat všechna pravidla z vrstev. To může být velmi nebezpečné, pokud si nedáte pozor!

html {
    color: black; 
    /* tato barva vyhraje, i když je první */
}

@layer framework {
    html {
        color: white;
    }
}

@layer theme {
    html {
         color: dark-red;
    }
}

Stejná priorita platí i pro pravidla ve vrstvě a vnořených vrstvách. To znamená, že například pravidla ve vrstvě framework budo mít vyšší prioritu než pravidla ve vrstvách framework.base a framework.theme:

@layer frawork {
    html {
        color: black; 
        /* tato barva vyhraje, i když je první */
    }
    
    @layer base { /* = framework.base */
        html {
            color: white;
        }
    }
    
    @layer theme { /* = framework.theme */
        html {
             color: dark-red;
        }
    }
}

Pozor na !important

Klíčové slovo !important do teď fungovalo tak, že přepisovalo všechna pravidla bez něj a z pravidel s ním vyhrálo vždy to poslední.

Přidání vrstev tohle chování mění a !important funguje jako negace pořadí priority vrstev. Pokud tedy stejnému prvku nastavíte !important v několika vrstvách, vyhraje vrstva s nejnižší prioritou. Tohle chování není nové, ale do teď fungovalo jen jako negace stylů stránky, prohlížeče a operačního systému – např. OS v režimu vysokého kontrastu mohl pomocí !important změnit barvy všech stránek.

@layer reset {
    html {
        color: white !important;
        background: black !important;
    }
}

@layer framework {
    html {
        color: black !important;
        background: white !important;
    }
}    

@layer theme {
    html {
        color: dark-blue;
        background: light-yellow;
    }
}    

V tomto příkladu bude pořadí vrstev reset, framework a theme, ale barva se bude aplikovat v pořadí theme, framework a reset a tudíž bude stránka zobrazena jako bílá na černém pozadí, protože vyhraje styl reset.

V důsledku toho mají !important pravidla uvedená bez vrstvy nejnižší prioritu:

@layer theme {
    html {
        color: red !important; /* vyhraje */
    }
}

html {
     color: black !important; 
     /* důležité, ale ne více než theme */
}

Je tedy důležité dát si pozor při importu existujících CSS souborů, protože @import soubory do pojmenované nebo anonymní vrstvy zvýší význam !important oproti !important stylům uvedeným v hlavním souboru bez vrstvy.

Dědičnost

Dědičnost stylů je důležitou součástí CSS, ale není součástí kaskádových vrstev.

To znamená, že pokud prvek nemá nadefinovaný styl pro určitou vlastnost, zdědí ji od svého rodiče, přičemž se dědí výsledná hodnota a nikoliv hodnota ze stejné vrstvy (jelikož nedefinovaná vlastnost nemůže být přiřazena do vrstvy).

Stejně funguje i klíčové slovo inherit, které dědí výslednou vlastnost rodiče nezávisle na vrstvách:

@layer framework.theme {
    form { color: white; background: black; }
}
@layer theme {
    html { color: black; }
    button { color: inherit; }
}

V příkladu bude mít tlačítko černou barvu textu, ale ve formuláři bude mít bílou barvu textu, protože ji zdědí od formuláře, i když je tato barva definována v jiné vrstvě s nižší prioritou.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.

Tato stránka používá Akismet k omezení spamu. Podívejte se, jak vaše data z komentářů zpracováváme..