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

Nativní proměnné v CSS jsou fajn. Od proměnných v preprocesorech, které možná někteří znáte více, se liší tím, že se počítají přímo v prohlížeči a že jsou dostupné kromě CSS také v JavaScriptu a HTML.

Pokud jde o moderní prohlížeče, můžeme říci, že CSS proměnné mají plnou podporu.

Co je to?

Základy si ukážeme na jednoduchém příkladu. Definice proměnné vypadá následovně:

html {
  --color: blue;
}

Rovnou tady trochu nabořím pojmenování „proměnná v CSS“. Tímto způsobem totiž definujeme Custom Property – volitelnou nebo též autorskou vlastnost. Musím to zmínit. A jak sami vidíte, zápis opravdu odpovídá spíše vlastnosti, než proměnné.

Následuje použití vlastnosti už ve formě proměnné pomocí funkce var():

.box {
  color: var(--color);
}

Tuhle základní konstrukci si můžete vyzkoušet v mé ukázce.

V příkladu jsem definoval něco jako globální proměnnou, prostě ji navázal na nejvyšší prvek DOMu, tedy <html>.

Občas je v jiných příkladech vidět použití pseudoprvku :root. To je to samé jako selektor html, jen je váha (specificita) selektoru vyšší, na úrovni třídy. To se může hodit.

Proč „volitelné vlastnosti“ a ne prostě jen „proměnné“?

Zastavme se ještě na chvíli u pojmenovávání. Specifikace definuje „custom property“ (volitelnou vlastnost) a „variable“ (proměnnou) jako dvě odlišné věci.

Volitelné vlastnosti jsou ty vlastnosti, které nejsou zapsány v CSS specifikaci a mohou si je vymyslet a používat autoři a uživatelé webů. Vypadají jako vlastnosti, ale na začátku mají dvě pomlčky. V příkladu to je --color: blue.

Proměnné pak zpřístupňují hodnoty uložené ve volitelných vlastnostech. To je ona konstrukce var(--color), která vrací hodnotu blue nebo jinou nastavenou.

Teoreticky byste tedy v příkladu výše mohli založit novou vlastnost (--color: blue), ale ve funkci var() ji nepoužít. Mohli byste například hodnotu vlastnosti chtít jen sdílet v HTML nebo JavaScriptu. Pak jistě chápete, že nemůžeme mluvit o CSS proměnné.

Limity „autorských vlastností“ (#limity)

Mluvit jen o „CSS proměnných“ je nepřesné i proto, že je nemůžeme používat jako proměnné univerzální. Použití jen možné jen tam, kde vkládáte hodnoty standardních CSS vlastnosti. V Media Queries to například nehrozí:

/* Smůlička, následující nefunguje: */

@media --var(breakpoint-medium) {  }

.el { --var(jmeno-vlastnosti): 1px solid red ; }

--var(jmeno-selektoru) {  }

Některým to tady opět může začít připadat jako typické CSS: škrábeme se levou nohou za zadním uchem. Je potřeba si uvědomit, že programátorské hlavy mezi námi mají trochu jiné očekávání od slova „proměnná“ než pouhé využití autorem vymyšlené CSS vlastnosti.

Takoví čtenáři budou jistě nadšení chystanou specifikací od autorů CSS, která se jmenuje „CSS Environment Variables“. Zápisem env() by pak bylo možné nahradit jakoukoliv část CSS. A to je něco, co, na rozdíl od CSS proměnných, asi opravdu všichni chceme.

„CSS proměnné“ versus proměnné z preprocesorů

Proč se vlastně zabývat autorskými vlastnostmi v CSS, když už většina z nás používá proměnné v CSS preprocesorech jako je Sass?

Protože to jsou dvě odlišné věci. Volitelné vlastnosti z CSS se od preprocesorových proměnných liší následovně:

  • jsou dynamické, počítají se až v prohlížeči,
  • můžete k nim přistupovat nebo je číst také z HTML a JS,
  • řídí se dědičností, kaskádou a dalšími přirozenými vlastnostmi CSS.

Přinášejí tedy vlastnosti, která preprocesorové proměnné neumí. Na druhou stranu – CSS proměnné nemají jiné vlastnosti, kterými naopak disponují ty preprocesorové. Jak už jsem ukazoval, neumožní vám například vložit proměnnou kamkoliv do CSS kódu. Hlavně u větších projektů se bez CSS preprocesorů stále neobejdete.

Ideální variantou se tedy jeví propojit obojí: z preprocesorových proměnných, které užijete i v prohlížeči, generovat volitelné vlastnosti, podobně jako to dělá třeba framework Bootstrap.

V preprocesoru Sass definujeme interní proměnné:

$colors: (
  "blue": $blue,
  "indigo": $indigo,
  "purple": $purple,
) !default;

Ty pak můžeme dát k dispozici do autorských vlastností:

:root {
  --blue: map-get($colors, $blue);
  --indigo: map-get($colors, $indigo);
}

Díky zkompilovanému CSS pak budou k dispozici v prohlížeči a tedy také v HTML nebo JS:

:root {
  --blue: #007bff;
  --indigo: #6610f2;
}

Související: CSS preprocesorypoužívání proměnných v nich.

Překvapivá škála možných hodnot

Různorodost možných hodnot je velká:

:root {
  --color: #f00;
  --header-height: 68px;
  --line-height: 1.35;
  /* Jako existující vlastnost: */
  --padding: 10px 20px;
  /* Hodnota jako existující vlastnost: */
  --selector: padding;
  /* Text pro vlastnost content: */
  --text: "text";
  /* Hodnota jiné proměnné: */
  --a: var(--text);
  /* Libovolný výpočet: */
  --height: calc(2vh + 20px);
  /* Libovolný výraz v JavaScriptu: */
  --javascript: if(x > 5) this.width = 10;
}

Funkce var()

Podívejme se teď více na funkci, která umí z volitelné vlastnosti vytáhnout její hodnotu. Tady už můžeme konečně trochu mluvit o proměnných.

Použití už jste viděli:

.box {
  color: var(--color);
}

Fallbacky ve funkci var()

Cokoliv je za čárkou, považuje funkce za náhradní řešení pro případ, že první hodnota není definovaná:

.box {
  background: var(--bg-color, black);
  background: var(--bg-color, var(--primary-color));
}

Je tam samozřejmě možné mít čárek více, ale to jen pro případ, že jako fallback potřebujeme hodnoty oddělené čárkou. Hodí se například pro specifikaci barev v gradientech:

.box {
  --box-bg-color: var(--gradient, yellow, orange);
  background: linear-gradient(var(--box-bg-color));
}

Však si to zkuste naživo v následující ukázce.

Co když není proměnná definovaná?

Vezměme tuto hypotetickou situaci:

.box {
  color: blue;
  color: var(--my-color);
}  

Proměnnou --my-color jsme zde zapomněli definovat. Jakou barvu bude text mít?

Možná vás to překvapí, ale černou. Zdědí ji po výchozím nastavení pro text. Z pohledu prohlížeče to vypadá tak, že modrá (blue) byla předefinovaná proměnnou (var(--my-color)) . To si pamatujte.

Fakt, že hodnotu proměnné prohlížeč nezná, jej nezajímá. Mohli byste mu ji totiž hned v následující vteřině podstrčit třeba JavaScriptem. Ale pokud jste CSS proměnnou var(--my-color) zatím nedefinovali, aplikuje běžnou dědičnost a vezme barvu z rodičovského prvku.

Modrou barvu v příkladu ale zobrazí prohlížeč, který proměnné nepodporuje.

Pokud bychom chtěli fallback i pro podporující prohlížeče, musíme využít standardního mechanismu fallbacku za čárkou, který funkce var() nabízí:

.box {
  color: blue;
  color: var(--my-color, blue);
}

Živá ukázka hned následuje.

Slučování hodnot a matematika ve funkci var()

Programátoři, tady buďte opět opatrní. Jste v CSS, nikoliv programovacím jazyce. Nic jako v následujícím příkladu fungovat nebude:

.box {
  --gap: 20;
  /* Smůla, fungovat nebude: */
  margin-top: var(--gap)px;
}

Výsledkem by byla hodnota s mezerou: margin-top: 20 px, které prohlížeč nebude rozumět.

Pro „přetypování“ namísto toho můžete použít funci calc():

.box {
  --gap: 20;
  margin-top: calc(var(--gap) * 1px);
}

Já vím… Já vím, co si vy programátoři myslíte. Ale z pohledu CSS má toto logiku. Pořád nezapomínejme, že toto nejsou plnohodnotné proměnné.

Pojďme si ještě ukázat další příklady s funkcí calc(). Použít nějakou matematiku je zde možné:

.box {
  --gap: 20px;
  margin-top: calc(var(--gap) + 1px); /* 21px */
  margin-top: calc(var(--gap) / 2); /* 10px */
}  

Psal jsem, že nám slučování hodnot neprojde. Slučování řetězců ale možné je:

.box {
  --text-hi: 'Ahoj';
  --text-sentence: var(--text-hi)', jsem ráda, že tě vidím!';
}

Sdílení v HTML

V CSS si můžeme například přednastavit stupnici hodnot:

html {
  --size-lg: 1.25rem;
  --size-sm: 0.8rem;
}

V HTML si pak vybrat jednu z nich:

<p style="font-size: var(--size-lg)">
  Lorem ipsum…
</p>

Daleko zajímavější je ale změna hodnoty vlastnosti z HTML. V CSS si definujeme vlastnost i její hodnotu:

html {
  --color: black;
}

p {
  color: var(--color);
}

V HTML ji pak změníme:

<p style="--color: blue">
  Lorem ipsum…
</p>

Oba příklady jsou samozřejmě děsně primitivní. Později ještě ukážu, jak je možné sdílení v HTML prakticky využít. Tady je CodePen obsahující oba zjednodušené scénáře.

Sdílení v JavaScriptu

Základní metody pro čtení nebo zápis jsou jednoduché:

// číst hodnotu
getComputedStyle(element).getPropertyValue("--color");

// zapsat hodnotu
element.style.setProperty("--color", "blue");

V jQuery od verze 3.1 to se „proměnnými“ pracuje stejně jako s běžnými CSS vlastnostmi. Aby ne, když to jsou vlastnosti, jen volitelné.

// číst hodnotu
$('#box1').css('--color');

// zapsat hodnotu
$('#box2').css('--color', 'blue');

Pokud tedy jQuery ještě používáte…

Podpora a fallbacky

CSS proměnné podporují všechny moderní prohlížeče. Bez podpory jsme například v Opeře Mini a ve všech Internet Explorerech. Jasně, obvyklí podezřelí. To vůbec nevadí. Více o podpoře je na webu CanIUse. caniuse.com/css-variables

Tolik k volitelným vlastnostem a CSS proměnným. Teď už vám můžu nabídnout jen sumarizaci toho nejdůležitějšího.

Shrnutí

Pojďme si sesumírovat klady a zápory volitelných vlastností a jejich použití jako proměnných v CSS.

  • Plus: Počítají se v prohlížeči a jsou dostupné z CSS, HTML i JS.
  • Minus: Není je možné použít na jakémkoliv místě v kódu jako preprocesorové proměnné. Protože to nejsou proměnné.

Pro praktického použití proměnných jděte na další článek.