Martin Michálek Martin Michálek  – 17. 7. 2019

„CSS proměnné“ (nebo také autorské vlastnosti) už známe z předchozího textu. V tomhle se podívejme na jejich praktické použití.

Autorské vlastnosti v CSS se vám budou hodit, když potřebujete okamžitou změnu jedné nebo více vlastností prvků: na základě interakce uživatele nebo třeba uvnitř Media Query.

Tady je šest příkladů, které jsem pro vás vybral:

  1. Změna jednoho parametru CSS vlastnosti
  2. Úprava barevného schématu
  3. Obarvení SVG vloženého do HTML
  4. Výpočet typografických hodnot ze základní mřížky
  5. Změna layoutu na breakpointech
  6. Sdílení hodnot breakpointů s JavaScriptem

A teď už vzhůru do nich.

1) Změna jednoho parametru CSS vlastnosti

Ne všechny hodnoty CSS vlastnosti můžeme rozbíjet na menší kousky. Vezměme třeba parametry u barevných přechodů. Nebo u stínování:

.box {
  box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.5);
}

První dvě hodnoty parametrů (5px 5px) představují posun stínu ve vodorovném a svislém směru.

Pokud bychom chtěli po najetí myši změnit jen posun stínu, musíme znovu uvést celou deklaraci k vlastnosti box-shadow.

.box:hover {
  box-shadow: 10px 10px 10px rgba(0, 0, 0, 0.5);
}

Trochu hloupé, že?

Ukažme si, jak v CSS změnit právě jen posun stínu. S proměnnými to půjde, žádné strachy. Nejprve si založíme a naplníme volitelnou vlastnost:

--box-shadow-offset: 5px;

Následně ji použijeme na konkrétní pozice parametrů stínu:

.box {
  box-shadow:
    var(--box-shadow-offset)
    var(--box-shadow-offset)
    10px
    rgba(0, 0, 0, 0.5);
}

A teď to kouzlo. Na interakci uživatele pak už nemusíme nastavovat všechny hodnoty vlastnosti box-shadow. Stačí změnit jen onu autorskou vlastnost:

.box:hover {
  --box-shadow-offset: 10px;
}

No není to hezké?

2) Úprava barevného schématu

Na podobném principu je postavený příklad se změnou barevného schématu pro lokální kontext.

Představme si řešení situace s inverzně zbarvenou komponentou na obrázku:

Jak to řešit pomocí obyčejného CSS? Barvy pro tmavou verzi bychom asi museli přepisovat pomocí kaskády:

.inverted {
  background: black;
  color: white;
}

.inverted a { /* bla bla, fuj */ }
.inverted .btn { /* bla bla, fuj */ }

U složitých komponent bude kód opravdu „fuj“. Vidle jsou pro takovou práci nepostradatelné, protože budeme přetěžovat mnoho deklarací.

Teď to zkusme s CSS proměnnými. Do autorských vlastností si uložíme jednoduché barevné schéma:

:root {
  --bg-color: white;  
  --text-color: black;
  --link-color: blue;  
}

Připomínám, že selektor :root zařídí, aby byly proměnné dostupné v celém dokumentu.

Následuje použití v typografické základně dokumentu (na prvku a) a v komponentách .box.button:

a {
  color: var(--link-color);
}

.box {
  color: var(--text-color);
  background: var(--bg-color);
}

.button {
  border-color: var(--link-color);
  color: var(--link-color);
}

Pro určitý kontext pak chceme tyto barvy změnit. Je jedno, zda se to týká celé stránky nebo jen její části. Založíme si pro ni třídu .inverted:

.inverted {
  --bg-color: black;
  --text-color: white;
  --link-color: yellow;  
}

V HTML se pak komponenta .box.inverted sama „bezbolestně“ přebarví.

Připomínám, že jsme ušetřili docela hodně kódu nutného k „přebíjení“ barev v inverzních komponentách bez použití CSS proměnných. Celá věc se nám tedy bude lépe spravovat.

3) Obarvení SVG vloženého do HTML

Představte si, že chcete do HTML vložit SVG, které je závislé na barevném schématu webu. Například ikony, u těch je to téměř jisté.

Z předchozího textu už víte, že CSS proměnné jsou dostupné v CSS i JavaScriptu, takže to nebude těžké. Tady je příklad.

Nejprve si v CSS do autorské vlastnosti uložíme barvu:

:root {
  --text-color: orange;
}

V HTML pak vlastnost použijeme v podobě CSS proměnné. Uvedu zjednodušený zápis struktury SVG:

<svg>
  <text fill="var(--text-color)">
    SVG
  </text>
</svg>

Raději upozorňuji, že zdrojový kód SVG souboru je nutné vložit přímo do HTML. Jinak nebudete přístup k proměnným mít.

Pojďme teď na další příklad, už složitější.

4) Výpočet typografických hodnot ze základní mřížky

Podobně jako u příkladu s barevným schématem, i zde chceme něco nechat počítat prohlížeč. CSS proměnné tady opět nahradí složitý zápis v kódu.

V tomto příkladu stavíme design projektu na neviditelné typografické mřížce.

Šířku i výšku základního rozměru – čtverce mřížky – si nastavíme na 0.25rem. Od něj odvozujeme základní výšku řádku a z ní také odvodíme velikost písma. Tady je to vyjádřené v autorských vlastnostech:

:root {
  --baseline: 0.25rem;
  --baseline-multiplier: 7;  
  --line-height: calc(var(--baseline-multiplier) * var(--baseline));
  --font-size: calc(var(--line-height) / 1.4);  
}

Vysvětleme:

  • --baseline – základní jednotka typografické mřížky je 0.25rem, což bude ve většině případů 4px. V typografii tedy budeme pracovat s násobky čtyř.
  • --baseline-multiplier – násobič pro základní výšku řádku se mění pro různé breakpointy. Výchozí je 7.
  • --line-height – základní výška řádku je díky součinu základní jednotky mřížky s násobičem nastavená na 1.75rem (28px).
  • --font-size – zde počítáme odvozením z výšky řádku. Text bude tedy vysázený ve velikosti 20px.

Pak už hodnoty jen používáme:

p {
  line-height: var(--line-height);
  font-size: var(--font-size);  
  margin-bottom: var(--line-height);
}

Zatím to je děsně složitá nuda, vím. Jenže teď přijde ten trik.

Na velikostech obrazovek, například od 640px, změním násobič:

@media (min-width: 640px) {
  :root {
    --baseline-multiplier: 10;  
  }  
}

A hotovo. Celá typografie se přepočte jako v obrázku nahoře.

Žádné trapné přepočítávání rozměrů všech typografických elementů pro tento daný breakpoint. Udělá se to „samo“. Změnou --baseline-multiplier se přepočte --line-height a z ní se přepočte --font-size. Vše stále pasuje do čtyřpixelové typografické mřížky.

Tohle řešení typografie zatím není u webových projektů běžné, ale vychází ze starých principů sázení písma. Takže věřím, že mnohým z vás bude časem k užitku.

Pokud byste chtěli složitější příklad, kde se násobič základní mřížky přepočítává i pro nadpisy, tady je: cdpn.io/e/wPXqYG

5) Změna layoutu na breakpointech

Layout potřebujeme v responzivním designu ovlivňovat dost často, ale měnit pokaždé všechny potřebné vlastnosti flexboxu nebo gridu může být pruda. Pojďme na to jinak, opět s pomocí proměnných v CSS.

Půjde o čtyřsloupcový layout, který se v jednotlivých rozmezích layoutu mění jako na obrázku.

HTML vypadá zhruba takto:

<div class="container">
  <p class="column"></p>
  <p class="column"></p>
  <p class="column"></p>
  <p class="column"></p>
</div>

V CSS si nastavíme flexboxový layout:

.container {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

Vysvětlím:

  • flex-wrap: wrap – pokud layout přeteče, může se zalomit.
  • justify-content: space-between – volné místo distribuujeme mezi položky flexboxu.

Jak ale zařídíme layouty, které vidíme na obrázku? Díky použití justify-content:space-betweenflex-wrap:wrap by nám mohlo stačit na breakpointech nastavovat šířku pro položku flexboxu a mezeru mezi nimi:

Breakpoint Šířka prvku .column Mezera mezi .column
0 - 479px 100% 0
480px - 767px 50% 1rem
768px+ 25% 1rem

V běžném CSS kódu bychom to asi museli tupě opsat a pro všechny breakpointy nastavit.

Druhá možnost jsou CSS proměnné – prostě si v dotčené části kódu nasadit vzorec pro výpočet šířky jednotlivých sloupečků:

.column {
  width: calc(
    var(--layout-column-width)
    - var(--layout-gap)/2
  );
}

A co že jsou ty proměnné --layout-column-width--layout-gap? Jasně – říkají jak široký budou prvky .column a jak bude široká bude mezera mezi nimi.

Výchozí stav uděláme pro malé displeje takto:

:root {
  --layout-gap: 0;
  --layout-column-width: 100%;  
}

A teď už jen měníme dva parametry uložené v proměnných pro jednotlivé breakpointy:

@media (min-width: 480px) {
  :root {
    --layout-gap: 1em;
    --layout-column-width: 50%;  
  }  
}

@media (min-width: 768px) {
  :root {
    --layout-column-width: 25%;  
  }  
}

Hodí se to hlavně v případech, kdy jsou na webu změny layoutu časté a opakování flexboxových vlastností na všech breakpointech by kód komplikovalo.

Poznámka k náhradním řešením

Tady nebude ani složité navrhnout fallback. Jako výchozí stav bychom napsali desktopové zobrazení, takže Internet Explorer 11 zobrazí layout v pořádku. Připomínám, že aktuálně jde o nejrozšířenější z prohlížečů bez podpory CSS proměnných.

Určitě jste si všimli, že jinak jsem v textu moc neřešil náhradní řešení pro Internet Explorer 11. Nechtěl jsem už tak dlouhý článek komplikovat. Věřím ale, že si vystačíte s obecnými strategiemi pro tvorbu fallbacků k autorským vlastnostem, které zmiňuji v předchozím článku.

6) Sdílení hodnot breakpointů s JavaScriptem

Z úvodu do CSS proměnných víte, že k volitelným vlastnostem kaskádových stylů je možné přistupovat z HTML nebo také z JavaScriptu. Jednu z hodnot, kterou je potřeba sdílet snad na každém projektu jsou breakpointy responzivního layoutu.

S pomocí CSS proměnných je problém definice na více místech možné částečně vyřešit. Ve stylech prostě nejprve nadefinujeme breakpointy:

:root {
  --breakpoint-xs-value: (max-width: 479px);
  --breakpoint-sm-value: (min-width: 480px) and (max-width: 639px);
  --breakpoint-md-value: (min-width: 640px);
}

A v JS je můžeme číst. Ukážu jednoduchý příklad s použitím jQuery:

var
 mqXS
    = window.matchMedia(
        $('html').css('--breakpoint-xs-value').trim()
      ),
 mqSM
    = window.matchMedia(
        $('html').css('--breakpoint-sm-value').trim()
      ),
 mqMD
    = window.matchMedia(
        $('html').css('--breakpoint-md-value').trim()
      );  

Do proměnných si prostě uložíme breakpointy responzivního designu získané přečtením hodnot volitelných vlastností. CSS je předává jako řetězce textů, což je přesně to co potřebujeme.

Pro fungování příkladu musíme javascriptového kódu napsat o fous více, ale nechci to komplikovat. Prostě se na něj podívejte do CodePenu, pokud budete mít zájem.

V příkladu pak máme breakpointy pro styly i skripty uložené v CSS, ale tam je nedefinujeme na jednom místě. Hned o pár řádku níže máme ve stylech toto:

@media (min-width: 480px) and (max-width: 639px) { … }

Asi vás napadne, proč uloženého breakpointy nevyužít nějak následovně, že?

@media var(--breakpoint-sm-value) {
  /* prdlajs */
}

Jak komentář napovídá, tohle není možné. CSS proměnné není možné využívat jinde než na místě hodnot vlastností.

Samozřejmě bychom tady mohli zapojit preprocesor a práci si usnadnit. Považuji to za dobrý scénář pro kombinaci preprocesorových a CSS proměnných. Napsal bych to nějak takhle:

$breakpoint-sm: "(min-width: 480px) and (max-width: 639px)";

:root {
  --breakpoint-sm-value: #{$breakpoint-sm};
}

@media #{$breakpoint-sm} {
  /* funguje */
}

Šestým příkladem skončíme. Chtěl jsem ukázat hlavně to, jak mohou proměnné zjednodušit kód a omezit jeho už tak repetitivní povahu.

Všechny příklady jsou také v kolekci na CodePenu: codepen.io/collection/DmROBE.

Jakými jiným způsobem používáte CSS proměnné vy?

Dejte mi vědět v komentářích na sociálních sítích nebo zde u článku.