Jak vytvořit tmavý režim / dark mode v CSS
Jak co nejlépe vytvořit pro web tmavý režim, aby automaticky respektoval nastavení uživatele.
- Tmavý, nebo světlý?
- Nastavení v systému/prohlížeči
-
Detekce v CSS
@media
pravidlu - Tmavý režim
- Světlý režim
- Zjištění podpory v JS
- Reakce na změnu režimu
- Best practice
- CSS proměnné
- Převod SASS proměnných
-
Co s
rgba()
- Fallback CSS proměnných
- Ruční přepínání
- Duplicitní deklarace barev
- Uložení nastavení
- Řešení obvyklých problémů
- Barvy ikon a obrázků
- Přebarvení CSS filtrem
- Velký počet barev
-
Barva prohlížeče
theme-color
- Jak je přechod náročný?
- Kde se inspirovat
- Odkazy jinam
V roce 2019 přišel Apple s tmavým režimem v iPhonech. To tmavé režimy značně zpopularisovalo.
Uživatelé si najednou nastavují tmavé zobrazení a očekávají, že se tomu přizpůsobí i web.
Tmavý, nebo světlý?
Jestli je lepší, čitelnější nebo pro oči lepší tmavý/světlý web je těžká otázka. Zdá se, že každému vyhovuje něco jiného.
Zastánci tmavých režimů mají pocit, že jim světlý web vypálí oči. Zastánci světlých zase argumentují lepší čitelností při intensivním okolním světle.
A potom je tu skupina těch, kteří preferují automatické nastavení podle denní doby.
Naštěstí se vhodným řešením dá zavděčit všem…
Nastavení v systému/prohlížeči
Lze se setkat se třemi možnostmi nastavení:
- světlé,
- tmavé,
- automatické
Automaticky znamená, že se podle denní doby (většinou východ/západ slunce) režim změní.
Ve Windows jde nastavit pouze tmavý nebo světlý vzhled aplikace. Nastavení → Přizpůsobení → Barvy → Zvolit výchozí režim aplikace:
Toto nastavení potom přebírají prohlížeče.
V macOS existuje i automatický režim.
V iOS potom obdobně:
Web podporující změnu režimu dle nastavení systému by se při změně měl ihned přebarvit.
Světlé nebo tmavé zobrazení jde obvykle nastavit i přímo v prohlížeči.
Detekce v CSS @media
pravidlu
Přímo v CSS existuje @media
pravidlo detekující preferovaný režim.
Tmavý režim
@media (prefers-color-scheme: dark) {
/* styly pro tmavý režim */
}
Světlý režim
@media (prefers-color-scheme: light) {
/* styly pro světlý režim */
}
Podpora v prohlížečích je slušná. Chybí akorát v marginálních prohlížečích jako je IE 11 nebo starý Edge.
Pokud se ale veškeré styly neuzavřou do těchto podmínek, ale jeden z nich se nechá mimo, zůstane v nepodporovaných prohlížečích alespoň výchozí styl.
Zjištění podpory v JS
Detekce v JavaScriptu lze potom provést stejnými media pravidly v matchMedia
:
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
// uživatel preferuje tmavý režim
}
else {
// uživatel preferuje světlý režim
}
Hodí se i detekce, jestli prohlížeč dokáže převzít informaci o nastavení ze systému/prohlížeče.
if (window.matchMedia('(prefers-color-scheme)').media === 'not all') {
// tmavý/světlý režim není podporován
}
A na základě toho (ne)zobrazovat přepínání mezi světlým a tmavým tématem.
Reakce na změnu režimu
Na změnu režimu v systému/prohlížeči lze reagovat následovně:
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
if (event.matches) {
// přepnuto na tmavý
}
else {
// přepnuto na světlý
}
})
Pro Safari je v roce 2020 potřeba volit jiný postup a použít addListener
bez názvu události, který funguje i v ostatních prohlížečích:
window.matchMedia('(prefers-color-scheme: dark)').addListener(event => {
if (event.matches) {
// přepnuto na tmavý
}
else {
// přepnuto na světlý
}
})
Tato událost se zavolá ve 2 případech:
- Uživatel si přepne v nastavení systému/prohlížeče preferovaný režim.
- Režim se změní sám (např. automatická změna v iOS podle denní doby).
Best practice
Jak ale v praxi nejlépe tmavý režim vytvořit?
Nejjednodušší řešení je přidat pár řádků kódu, které pomocí CSS filtrů otočí barvy:
@media (prefers-color-scheme: dark) {
html {
background: #1D1D1D;
filter: invert(100%) contrast(90%) hue-rotate(180deg);
}
}
Pro seriosní použití to ale moc není :–)
Když pominu to, že některé barvy a obrázky nedopadnou takovou změnou dobře, další věc je, že nestačí jen invertovat barvy.
Barvy totiž slouží i k znázornění důležitosti a fakt, že jde zdůraznit část stránky použitím světlejší barvy, platí stejně u tmavého i světlého webu stejně – tato logika není invertovaná.
Proto je pro dobrý výsledek dobré barvy nastavit ručně.
CSS proměnné
Pro pořádné vytvoření tmavého režimu (nebo obecně snadné změny stylu) je ideální používat CSS proměnné.
Pokud se tmavý režim doplňuje do již hotového webu, je vhodné na ně přejít. Použití je potom následující:
:root {
--background-color: #ededed;
--text-color: #212121;
}
@media (prefers-color-scheme: dark) {
:root {
--background-color: #111;
--text-color: #ededed;
}
}
body {
background-color: var(--background-color);
color: var(--text-color);
}
-
Nejprve se nadeklarují výchozí proměnné světlého režimu pro selektor
:root
.Bývá zvykem pro CSS proměnné používat kořenový selektor
:root
, ten u HTML stránky odpovídá selektoruhtml
, jen je silnější (ukázka).Teoreticky je universálnější – třeba kdyby se styly potom připojily do
<svg>
, kde žádný<html>
element není. V praxi se tedy může použít klidně ihtml
. -
Potom se pro preferovaný tmavý režim proměnné přepíší na tmavé varianty.
-
Nyní se všude v kódu pro barvy používají CSS proměnné.
Převod SASS proměnných
Pokud se na webu používaly proměnné v preprocesoru (např. SASS):
a {
color: $color-link;
}
Je možné je převést na použití var(--color-link)
nahrazením tohoto regulárního výrazu \$(color[\w-]*)
na var(--$1)
.
Co s rgba()
V případě preprocesorů je běžné psát pro průhledné barvy:
a {
color: rgba($color-link, .7);
}
S CSS proměnnou nastane problém, protože tohle na ní nejde aplikovat. Řešení je třeba převod do RGB formátu. Jde k tomu použít jednoduchý SASS mixin. A proměnnou barvy potom použít uvnitř rgba
:
@function hexToRgb($color) {
@return red($color), green($color), blue($color);
}
:root {
--color-rgb-background: #{hexToRgb(#666)};
}
div {
background: rgba(var(--color-rgb-background), .5);
}
Fallback CSS proměnných
Protože třeba v IE 11 CSS proměnné nefungují, hodí se použít nějaký fallback. Existuje šikovný PostCSS plugin postcss-css-variables. Ten dokáže vlastnosti s proměnnými zduplikovat a var(--color-neco)
nahradit skutečnou hodnotou.
postcss([
cssvariables({
preserve: true,
}),
])
Hodit se může i možnost nakonfigurovat proměnné přímo:
postcss([
cssvariables({
preserve: true,
variables: {'--color-background', '#111')},
preserveInjectedVariables: false,
}),
])
Výsledkem je potom následující kód:
neco {
background: #111;
background: var(--color-background);
}
Nové prohlížeče použijí proměnnou, staré fallback.
Ruční přepínání
Říká se, že každá položka v nastavení je selhání designéra. Z tohoto pohledu se nabízí žádnou možnost přepínání mezi vzhledy nemít a nechat to na nastavení systému/prohlížeče.
Je otázka, proč by člověku, co má v systému nastaven tmavý režim, vadilo, že se tomu přizpůsobí web.
Nabízí se jen situace, kdy jsou uživatelé zvyklí na světlý web a najednou se jim přepne do tmavé podoby.
Pro možnost autodetekce i ručního přepínání zároveň je vhodné použít nějaký mixin vkládající kód pro vynucený i automatický tmavý režim:
@mixin inDark {
.theme-dark {
@content;
}
.theme-system {
@media (prefers-color-scheme: dark) {
@content;
}
}
}
A použití:
@include inDark {
--color-background: #111;
}
To zajistí následující chování:
-
Bude-li mít značka
<html>
třídutheme-dark
, nastaví se tmavé proměnné. -
Bude-li mít značka
<html>
třídutheme-system
, nastaví se tmavé proměnné jen když bude uživatel preferovat tmavý režim. -
Nebude-li mít
<html>
žádnou třídu, použijí se výchozí světlé barvy.
Nyní stačí už jen přepínat třídy JavaScriptem.
Duplicitní deklarace barev
Jak je vidět z kódu, deklarace CSS proměnných bude v kódu dvakrát. Mohlo by se nabízet detekovat styl čistě v JS a až podle toho třídu nastavovat. Bohužel to má nevýhodu v problikávání při načítání, než se stihne JS spustit.
Takže jedině takový JS nedávat do externího souboru, ale umístit ho do kódu před samotný obsah webu.
Uložení nastavení
K úvaze je, kam uložit nastavení vzhledu. Záleží na typu aplikace. U klasické aplikace renderující se na straně serveru jsou výhodnější cookies než třeba localStorage
, protože kvůli prevenci probliknutí po změně je nutné znát situaci už na backendu.
U SPA je localStorage
v pohodě.
Nastavení se i může ukládat do profilu, pokud se uživatel přihlašuje.
Řešení obvyklých problémů
Při převádění již existujícího světlého webu do tmavých barev dost možná narazíte na následující problémy:
Barvy ikon a obrázků
Asi nejpracnější část výroby tmavého režimu, pokud nejsou ikony řešeny vhodným způsobem.
Ideální je používat nějaké ikony, které jde snadno přebarvovat. Takže třeba SVG vložené inline do HTML kódu nebo pomocí <use>
z SVG spritu:
<svg>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="sprite.svg#icon"></use>
</svg>
A v ikoně používat pro přebarvitelné části v atributech fill
a stroke
hodnotu currentColor
. Šlo by i používat přímo CSS proměnné, ale byl by s nim problém kvůli nepodpoře ve starších prohlížečích.
Potom už jde ikonky snadno přebarvit:
svg {
fill: var(--color-text);
}
Dobře přebarvitelné jsou i staré dobré font ikony, ale ty trpí řadou různých problémů, že je používat nedoporučuji.
Přebarvení CSS filtrem
U jiných než vektorových obrázků (PNG, JPG, GIF) je jediná možnost použít již zmíněnou vlastnost filter
.
Průhledný obrázek jako třeba logo jde převést na bílou následovně:
.theme-dark .logo {
filter: invert(.5) brightness(2);
}
Případně obráceně na černou:
.theme-dark .logo {
filter: invert(.5) brightness(0);
}
Velký počet barev
Hodně webů trpí nešvarem v podobě velkého počtu barevných odstínů. Většinou to vzniká tak, že se barvy chaoticky střílí od oka. Ve finále jsou potom na webu vysoké desítky až stovky různých barev.
Je dobré se podívat na všechny použité barvy – třeba nástrojem CSS Color Extractor – a pokusit se je sjednotit.
Usnadní to práci a udržování dvou (popř. více) barevných schémat.
Barva prohlížeče theme-color
Zvlášť na mobilech je populární přebarvovat horní lištu.
<meta name="theme-color" content="#1081DD">
I tuto lištu se pravděpodobně hodí barvit podle použitého režimu, aby ladila ke zbytku stránky.
Není problém barvu přepínat JavaScriptem, takže může reagovat i na přepínání a detekci režimu.
Akorát to nejde jen v CSS, ale musí se skriptem měnit atribut content
– živá ukázka.
Jak je přechod náročný?
Ačkoliv to tak může vypadat, vytvořit tmavý režim k již existujícímu světlému webu není úplně práce na odpoledne. A může vyžadovat velkou porci změn ve zdrojovém kódu.
Příklad z jednoho projektu:
Kde se inspirovat
Jaké zvolit odstíny černé a šedé? Příklad populárních webů a aplikací, kde existuje světlý/tmavý režim.
Komentáře