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) {
  /* Kód pro prohlížeče, které 
     podporují grid-template-rows:masonry */
}

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 v březnu 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 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) {
  /* CSS kód */
}

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) {
  /* CSS kód */
}

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. Asi víte proč. Autoři specifikace se poučili z nejasného použití čárky namísto or v dotazech na media, Media Queries. vrdl.cz/p/css3-media-queries

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 */
}

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. github.com/postcss/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 */
}

Poněkud blbé ale je, že tuto funkci nepodporuje Safari, alespoň ne v březnu 2021.

Museli bychom se tak dotazovat na podporu funkce selector(), kterou bychom se dotazovali na podporu konkrétního typu selektoru. Trochu inception.

Funkce selector() je ale součástí až nové verze specifikace modulu „CSS Conditional Rules“, takže se budeme tvářit, že se nepodpoře ze strany Safari zatím nedivíme.

Řešení pro podporu detekce 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 tradici „CSS hacků“, kterou jsme zažili 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í @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í:

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, po kterém jsme 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 navíc 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ářů.

Komentáře

Váš názor? Vaše zkušenosti? Našli jste chybu?