Martin Michálek Martin Michálek  – 8. 3. 2021

V CSS nebo JavaScriptu se nám občas hodí napsat kód, který rozlišuje varianty podle podpory určitých CSS vlastností.

K tomu můžeme použít poměrně nové pravidlo @supports, součást specifikace „CSS Conditional Rules Module“.

S pomocí @supports je možné psát „feature queries“, dotazy na vlastnosti. Vezměme například ukázky s dotazem na nativní podporu layoutu typu masonry:

@supports (grid-template-rows: masonry) {
  .text {
    color: green;
  }
}

Pokud se na ukázku podíváte v prohlížečích, které podporují vlastnost grid-template-rows a zároveň její hodnotu masonry, což je i koncem roku 2021 zatím jen Firefox Nightly, uvidíte zelený text.

Ve všech ostatních prohlížečích bude text černý.

Všimněte si důležité věci – pro detekování podpory se musíme dotazovat na vlastnost i její hodnotu. Takže pokud bychom se ptali na podporu vlastnosti display, musíme se zeptat například na display:block.

Logické operátory: not, andor

Specifikace „Feature Queries“ velmi správně definuje logické operátory podle zvyklostí z jiných jazyků.

Operátor not

Velice často se nám může hodit negace a klíčové slovo not:

@supports not (display: grid) {
  .box {
    float: right;
  }
}

Kód se aplikuje jen v těch prohlížečích, které nepodporují CSS grid.

Operátor and

Logický operátor konjunkce – and:

@supports (display: table-cell) and (display: list-item) {
  .text {
    color: green;
  }
}

Použijeme v případech, kdy musí platit všechny podmínky.

Operátor or

Další operátor – or – definuje logickou disjunkci:

@supports (transform-style: preserve) or 
  (-moz-transform-style: preserve) {
  .text {
    color: green;
  }
}

Podmínka je platná v případě, že prohlížeč podporuje alespoň jednu z kombinací vlastnost a hodnota uvedených v závorkách. To se může hodit právě pro práci s prohlížečovými prefixy, zde -moz. Píšu o tom ještě v textu níže.

Kombinování operátorů

Při kombinování operátoru se vyplatí být doslovný – vždy uvádějte závorky.

Tyto deklarace nejsou validní:

@supports display: flex {
  /* Neplatné */
}

@supports (transition-property: color) or
  (animation-name: foo) and
  (transform: rotate(10deg)) {
  /* Neplatné */
}

Tento zápis ale validní je:

@supports (display: flex) {
  /* … */
}

@supports ((transition-property: color) or
  (animation-name: foo)) and
  (transform: rotate(10deg)) {
  /* … */
}

Aby nedošlo k záměně mezi andor, syntaxe je specifikována tak, aby byly výslovně psány jako and nebo or.

Prohlížečové prefixy

Dalším překvapením může být nutnost používat všechny prefixové vlastnosti. Pokud máte v cílové skupiny uživatele prohlížečů, které vlastnost podporují jen s použitím prefixů, musíte je uvést všechny:

@supports ((box-shadow: 0 0 2px black inset) or
  (-moz-box-shadow: 0 0 2px black inset) or
  (-webkit-box-shadow: 0 0 2px black inset) or
  (-o-box-shadow: 0 0 2px black inset)) {
  /* Kód pro všechny prohlížeče podporující box-shadow, včetně už historických */
}

Příklad mám ze specifikace. Dneska už byste prefixy k box-shadow nepotřebovali, takže tím kromě jiného ilustruji, že prefixy je potřeba uvádět jen tehdy, pokud byste je psali i v běžném CSS kódu.

Nejlepší je ale starost o prefixy přenechat automatizaci, konkrétně nástroji Autoprefixer.

Detekce podpory selektorů

Některé moderní prohlížeče umožňují detekovat také podporu určitých CSS selektorů. Dělají to pomocí nové funkce selector():

@supports selector(A > B) {
  /* Kód pro prohlížeč, který podporuje selektor A > B */
}

Funkce selector() je součástí nové verze specifikace modulu „CSS Conditional Rules“ a podporují ji všechny moderní prohlížeče.

Řešení pro podporu detekce selektorů už dříve hledalo více autorů, pomocí různých hacků. Zajímavý je například tento, který se dotazuje na podporu pseudotřídy :placeholder-shown:

.foo { color: red }

:not(*):placeholder-shown,
.foo {
  color: green
}

V textu „Using Feature Detection, Conditionals, and Groups with Selectors“ světu své řešení představil „náš“ Jirka Vebr. Tož díky! vrdl.in/vebrfeature

Feature Queries v JavaScriptu

Metoda CSS.supports() vrací booleovskou hodnotu určující, zda prohlížeč danou funkci CSS podporuje nebo ne.

Toto jsou možné zápisy:

result = CSS.supports("grid-template-rows", "masonry");
result = CSS.supports("display: grid");
result = CSS.supports("(--foo: red)");
result = CSS.supports("(transform-style: preserve) or (-moz-transform-style: preserve)");

V prvním řádku vidíme verzi se dvěma parametry. V prvním je prostě hodnota a ve druhém vlastnosti.

Další tři řádky ukazují variantu, kdy do textu uvedeme DOMString rovnou s hodnotou celé podmínky.

Ti z vás, které jsem ještě neunavil detailním líčením, si možná všimli detekce autorské vlastnosti (nebo též „custom property“ nebo též „CSS proměnné“) ve třetím řádku ((--foo: red)). Ano, i tu je možné detekovat.

CSS hacky a progressive enhancement

Udělejme si teď pro zajímavost historickou výpravu. @supports totiž navazuje na silnou epochu „CSS hacků“, kterou jsme k všeobecné nelibosti prožívali zhruba v první dekádě dvacátého prvního století.

Tehdy nebylo možné v CSS podporu vlastností detekovat, proto kodérky a kodéři hledali chyby v prohlížečích při implementaci CSS pravidel. Takový zápis, který funguje v určitých prohlížečích a v jiných naopak ne.

Asi nejznámější byl podtržítkový hack:


.box {
  /* Kód pro všechny prohlížeče: */
  position: fixed;
  /* Kód jen pro IE5+: */
  _position: absolute;
}

Pokud by vás tahle, dnes už nechvalně známá praxe, zajímala, píšou o ní hezky v přehledu CSS hacků na tehdy populárním Interval.cz. interval.cz/clanky/css-hacky-prehled/

Brrr, úplně mě běhá mráz po zádech, když si představím, že v té době jsem nakódoval opravdu hodně webů. Jak bych tehdy byl za @supports vděčný!

CSS hacky a dnes @supports jsou důležitou částí zásadní webařské techniky – postupného vylepšování (progressive enhancement). Funguje asi takto:

  1. Vyrobíte základní řešení fungující ve všech prohlížečích.
  2. Nad tím postavíte lepší řešení fungující jen v některých prohlížečích.

Mezi jednotlivými řešeními je detekce vlastností (nikoliv prohlížeče!), například právě pomocí dotazu @supports.

Výsledkem je, že nějaké řešení máte pro nejširší možnou skupinu zařízení. Pro web ideální.

Podpora @supports a limity použitelnosti

První omezené použití @supports vychází z principů fungování prohlížečů – „feature queries“ nelze použít ke kontrole, zda prohlížeč vlastnost, hodnotu nebo selektor podporuje správně a bez chyb.

Pokud si totiž prohlížeč „myslí“, že vlastnost umí, vrátí na dotaz kladnou odpověď. Ale soudruzi z NDR mohli někde v implementaci vlastnosti udělat chybu.

Dalším omezením je samotná podpora vlastnosti, v tomto případě ale záleží na použití:

@supports nepodporuje žádný Internet Explorer. caniuse.com/css-featurequeries

Je ovšem otázka, jak moc vadí nepodpora v Internet Exploreru. Mě nevadí. Hned vám řeknu proč.

Internet Explorer totiž je skoro vždy ve skupině prohlížečů, které vlastnost nezvládají. Proto vůbec nevadí, když nerozumí ani otázce, zda vlastnost zvládá.

Ukažme si to ještě na jednom příkladu.

Závěrečná ukázka s detekcí CSS gridu

Vezměme, že máme HTML s jedním kontejnerem a třemi položkami:

<div class="container">
  <div class="item">
    Item 1
  </div>
  <div class="item">
    Item 2
  </div>
  <div class="item">
    Item 3
  </div>  
</div>

Chtěli bychom položky prostě umístit vedle sebe, což s pomocí flexboxu uděláme takto:

.container {
  display: flex;
}

.item {
  flex: 1;
}

Z nějakého důvodu bychom ovšem v moderních prohlížečích chtěli použít CSS grid:

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

Je to samozřejmě naprosto schůdné, použijeme detekci pomocí @supports:

@supports (display:grid) {
  .container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
  }
}

Na pohled to bude vypadat stejně. Takhle jednoduchý kód se samozřejmě nevyplatí psát ve flexboxu i gridu. Budu ale předpokládat, že jde o základ pro využití pokročilejších vlastností gridu, kterých má požehnaně.

Tady nastává moment, pro který jsme si v demíčku šli. IE nejen že nezná display:grid a zároveň nezná @supports, takže tento blok kódu vynechá ze zpracování. A to je dobře.

V CodePenu uvidíte v prohlížečích, podporujících grid, zelené písmo. V ostatních je to červeně.

A to je, prosím pěkně, úplně vše, co jsem vám chtěl říct. Máte-li po ruce zajímavou ukázku využití @supports, neváhejte mě ji svěřit do komentářů.