Stap over naar Dark Mode

Wat ik nodig heb, is een makkelijke manier om Dark Mode goed toe te passen, voor op m’n blog en voor de projecten waaraan ik werk. Zelf vind ik Dark Mode fijn om in te werken en wil mijn klanten en hun klanten dat ook laten ervaren. Maar voor mijn klanten is de inspanning om Dark Mode te laten bouwen moeilijk te verantwoorden en daarom moet het voor ontwikkelaars makkelijk in te bouwen zijn. Helaas is goed gereedschap maken meestal niet zo makkelijk.

Example Light vs Dark Mode

Het belang van Dark Mode

Sommigen vinden Dark Mode een onbenullige feature: veel moeite voor weinig. Het zou zelfs meer schade veroorzaken dan goed doen. Het is daarom belangrijk dat je website luistert naar de instellingen van de gebruiker. En ja, er zijn features die belangrijker zijn. Dit is echter een Whataboutism argument en geen inhoudelijk bezwaar om ergens tegen te zijn. Het verraadt iemands voorkeur. Haal Dark Mode niet te snel van je wensenlijstje. Dark Mode zorgt voor 30% minder stroomgebruik op OLED schermen en het geeft rust voor de ogen in de avond en in een donkere omgeving omdat je ogen zich minder vaak hoeven aan te passen aan het licht. Daarnaast is het prettiger voor gebruikers die overgevoelig zijn voor licht.

Dark Mode toepassen, forceerde me ook om mijn kleurensysteem te versimpelen en de contrastwaardes erin te verankeren. In het verleden keek ik achteraf, als een soort van sluitpost, of er voldoende contrast was en repareerde dat; geestdodend met grote kans op fouten. Tegenwoordig leg ik dat bij het begin vast in de hiërarchie van kleuren. Mijn persoonlijke blog, simpel van structuur en waaraan ik al jaren niet meer had gewerkt, was in een uur omgebouwd naar Dark Mode.

Dark Mode is niet zomaar een instelling

Dat gebruikers het niet zullen instellen, speelt niet. Voor welke instelling dan ook, zodra iets een instelling is, vindt en gebruikt enkel een groep ervaren gebruikers het. Maar Dark Mode is niet zomaar een instelling. Bij elke nieuwe iPhone en Mac of een update van het OS vraagt het je of je Light Mode, Dark Mode of een automatische tijd-wissel wilt gebruiken. Dit is al voldoende reden om Dark Mode serieus te nemen. Goede statistieken over het gebruik heb ik nog niet kunnen vinden. Wel zijn er hints dat Dark Mode gewaardeerd wordt. Ook in onze organisatie steekt zo’n 80% z’n hand op als ik vraag wie Dark Mode gebruikt. Dit betekent niet dat je je website alleen maar in Dark Mode moet gaan aanbieden. Nogmaals, laat je website luisteren naar de voorkeuren van de gebruiker. Er zijn namelijk personen die liever Light Mode gebruiken, zoals zij die zwart op wit gewoonweg mooier vinden of gebruikers die veel last hebben van Astigmatisme en waarvoor Dark Mode heel vervelend is. Astigmatisme wordt veroorzaakt door onregelmatige kromming van het hoornvlies of afwijkingen in de ooglens, zodat het licht niet goed verdeeld wordt op de retina. Je ziet dan een ‘glow’ rondom lichte tekst op een donkere achtergrond. Het Astigmatisme effect is ernstiger bij minder licht. De meeste mensen hebben meer of minder last van Astigmatisme, maar dat staat Dark Mode niet in de weg. Zelf heb ik ook een beetje last van Astigmatisme en toch een voorkeur voor Dark Mode, maar alleen als het goed is doorgevoerd.

Geen oplopende hiërarchie van tinten in een kleur

Elk project bezit een tint-hiërarchie per kleur: van licht naar donker of van donker naar licht, het is maar hoe je het bekijkt. Dat werkt prima voor Light Mode. Alleen bleek die volgordelijkheid in Dark Mode niet te werken. Het is geen éénrichtingsverkeer. In Dark Mode moet het eerst donkerder worden voordat het weer licht wordt. Daarnaast moeten de kleurnamen (namen van de variabelen) voor Light Mode en Dark Mode gelijk zijn, zodat je op één centrale plek in je CSS (root) de ‘inhoud’ van de variabelen kunt aanpassen en je dat niet per element hoeft te doen. Met alle gevolgen van dien. Zo kun je geen elkaar opvolgende cijfers gebruiken in kleurennamen, omdat dat verwarrend werkt. Wat is donkerder en lichter dan de achtergrondkleur? Wat heeft voldoende contrast? De naam van de kleurvariant moet beschrijvend zijn met ieder een eigen doel. Om een lang verhaal kort te maken, een goed werkende hiërarchie is: Root, Ground, Low, Moderate, High, Important en Critical. Voor een groot deel geleend uit Microsoft’s severity rating.

In de volgende voorbeelden gebruik ik de neutrale kleur Base. De tinten gebruik je ook bij andere kleuren: hiërarchie-kleuren (Primary, Secondary, Tertiary, Quaternary), status-kleuren (Success, Error, Warning, Selected, Focus, Highlight) en merk-kleuren (Dominant, Texture en Accent). De genoemde contrastwaardes gelden trouwens ook voor deze kleuren onderling.

Om te beginnen met Ground van de Base kleur.

Ground, een witte achtergrond wordt donker grijs en vooral niet zwart

Het is belangrijk om te begrijpen dat Dark Mode niet hetzelfde is als ‘Inverted Colors’. Wit wordt dus geen zwart en zwart wordt geen wit. Mocht je dat toch doen, ontstaan er een aantal problemen:

  1. Een zwarte achtergrond zorgt voor ‘smearing’ op OLED schermen. Een OLED scherm zet pixels die zwart zijn uit om zoveel mogelijk energie te besparen. Gekleurde, grijze of witte pixels zetten het licht voor die pixel weer (gedeeltelijk) aan. En omdat uit en aan zetten tijd nodig heeft, zal scrollen ‘smearing’ veroorzaken: witte tekst of andere elementen worden tijdelijk uitgesmeerd over je scherm.
  2. Een zwarte achtergrond zorgt ervoor dat je geen diepte meer kunt creëren met schaduw. Ook in Dark Mode moet schaduw de achtergrond donkerder maken. Een witte schaduw ziet er niet uit en creëert iets anders dan je zou willen.
  3. Witte tekst op een zwarte achtergrond is niet goed leesbaar. Dit komt doordat er minder licht in je ogen valt, waardoor je pupillen automatisch groter worden en je meer last krijgt van Astigmatisme. Het lijkt alsof witte tekst op een zwarte achtergrond een ‘glow’ krijgt. Om leesbaar te zijn moet tekst in Dark Mode dus veel minder contrast hebben dan in Light Mode.

De basis achtergrond, Ground, is in Dark Mode dus geen zwart, het is donker grijs. Dat zou er in je CSS zo uit kunnen zien:

:root {
    --color-base-ground: hsl(0, 0%, 100%);
}

@media (prefers-color-scheme: dark) {
    :root {
        --color-base-ground: hsl(0, 0%, 16%);
    }
}

.page {
    background-color: var(--color-base-ground);
}

Low voor vlakken om te groeperen en de hiërarchie te verduidelijken

Met een vlak dat net iets donkerder is dan de achtergrond groeperen ontwerpers vaak losse elementen in een user interface, zodat duidelijker is wat bij elkaar hoort. Zo’n vlak wordt wel ‘Box’ genoemd. Een item in een Box ligt op een lager niveau dan een item daarbuiten. Een Box ligt daarom ook gevoelsmatig dieper. En wat dieper ligt zou een donkerdere kleur moeten krijgen. Een Box mag niet te veel aandacht krijgen. Het helpt alleen, is niet vereist om een user interface te kunnen gebruiken en heeft daarom geen contrastvereisten.

Hiervoor is een kleur nodig die in Dark Mode niet zwart is, anders werkt een schaduw niet meer, en toch donkerder is dan Ground. Deze kleur noem ik ‘Low’. Low is zowel donkerder dan Ground in Dark en Light Mode.

:root {
    --color-base-ground: hsl(0, 0%, 100%);
    --color-base-low: hsl(0, 0%, 96%);
}

@media (prefers-color-scheme: dark) {
    :root {
        --color-base-ground: hsl(0, 0%, 16%);
        --color-base-low: hsl(0, 0%, 11%);
    }
}

.box {
    background-color: var(--color-base-low);
}

Moderate voor lijnen die niet essentieel zijn

Je kunt in Dark Mode maar zoveel tinten kiezen tussen Ground en Zwart om onderscheid te maken, zelfs voor mensen met goed zicht. Je moet op een bepaald moment kiezen om lichter te gaan dan Ground. Dat is Moderate. Moderate is in Light Mode uiteraard donkerder dan Ground en Low, en lichter dan Ground en Low in Dark Mode. Veelal kan je dit gebruiken voor lijnen die niet essentieel zijn om de user interface te kunnen gebruik, lijnen die enkel en alleen ondersteunend zijn.

:root {
    --color-base-ground: hsl(0, 0%, 100%);
    --color-base-low: hsl(0, 0%, 96%);
    --color-base-moderate: hsl(0, 0%, 82%);
}

@media (prefers-color-scheme: dark) {
    :root {
        --color-base-ground: hsl(0, 0%, 16%);
        --color-base-low: hsl(0, 0%, 11%);
        --color-base-moderate: hsl(0, 0%, 31%);
    }
}

.seperator {
    border-color: var(--color-base-moderate);
}

High voor vormen die relevant zijn

High is de eerste kleur die een contrasteis heeft. Minimale contrast met Low én Ground is 3. Deze contrastwaarde is goed genoeg (WCAG 2.0 level AA) voor grafische elementen of user interface elementen zoals een knop of invoerveld.

:root {
    --color-base-ground: hsl(0, 0%, 100%);
    --color-base-low: hsl(0, 0%, 96%);
    --color-base-moderate: hsl(0, 0%, 82%);
    --color-base-high: hsl(0, 0%, 55%);
}

@media (prefers-color-scheme: dark) {
    :root {
        --color-base-ground: hsl(0, 0%, 16%);
        --color-base-low: hsl(0, 0%, 11%);
        --color-base-moderate: hsl(0, 0%, 31%);
        --color-base-high: hsl(0, 0%, 45%);
    }
}

.text-input {
    border-color: var(--color-base-high);
}

Important voor vormen die noodzakelijk zijn

Important is een andere kleur die contrasteisen heeft. Minimale contrast met Low en Ground is 4.5. Deze contrastwaarde is goed genoeg voor lijnen, vlakken en voor grotere teksten (WCAG 2.0 level AA). Al hebben broodteksten en koppen een eigen set aan variabelen gekregen. Daar later meer over.

:root {
    --color-base-ground: hsl(0, 0%, 100%);
    --color-base-low: hsl(0, 0%, 96%);
    --color-base-moderate: hsl(0, 0%, 82%);
    --color-base-high: hsl(0, 0%, 55%);
    --color-base-important: hsl(0, 0%, 44%);
}

@media (prefers-color-scheme: dark) {
    :root {
        --color-base-ground: hsl(0, 0%, 16%);
        --color-base-low: hsl(0, 0%, 11%);
        --color-base-moderate: hsl(0, 0%, 31%);
        --color-base-high: hsl(0, 0%, 45%);
        --color-base-important: hsl(0, 0%, 57%);
    }
}

.icon {
    fill: var(--color-base-important);
}

Critical voor elementen die in alle situaties goed gezien moeten worden

Dit is de tint met de hoogste contrasteis. Critical krijgt een minimale contrastwaarde van 7 met Low en Ground. Dit zorgt ervoor dat alles waarvoor je dit inzet voldoet aan WCAG AAA. In de praktijk komt het er op neer dat je dit vooral zult gebruiken voor tekst met een speciaal doel, zoals ‘feedback text’ die je in een error kleur (rood) wilt tonen.

Hier houden de stappen op.

:root {
    --color-base-ground: hsl(0, 0%, 100%);
    --color-base-low: hsl(0, 0%, 96%);
    --color-base-moderate: hsl(0, 0%, 82%);
    --color-base-high: hsl(0, 0%, 55%);
    --color-base-important: hsl(0, 0%, 44%);
    --color-base-critical: hsl(0, 0%, 32%);
}

@media (prefers-color-scheme: dark) {
    :root {
        --color-base-ground: hsl(0, 0%, 16%);
        --color-base-low: hsl(0, 0%, 11%);
        --color-base-moderate: hsl(0, 0%, 31%);
        --color-base-high: hsl(0, 0%, 45%);
        --color-base-important: hsl(0, 0%, 57%);
        --color-base-critical: hsl(0, 0%, 71%);
    }
}

.button {
    background-color: var(--color-base-critical);
}

Root is wit of zwart

Misschien heb je opgemerkt dat Root is overgeslagen. De Root is meer een formaliteit dan dat het nodig is. De Root is niet anders dan wit in Light Mode en zwart in Dark Mode. Mogelijk kom je uitzonderlijke situaties tegen waarin je het toch nodig blijkt te hebben.

:root {
    --color-base-root: hsl(0, 0%, 100%);
    --color-base-ground: hsl(0, 0%, 100%);
    --color-base-low: hsl(0, 0%, 96%);
    --color-base-moderate: hsl(0, 0%, 82%);
    --color-base-high: hsl(0, 0%, 55%);
    --color-base-important: hsl(0, 0%, 44%);
    --color-base-critical: hsl(0, 0%, 32%);
}

@media (prefers-color-scheme: dark) {
    :root {
        --color-base-root: hsl(0, 0%, 1%);
        --color-base-ground: hsl(0, 0%, 16%);
        --color-base-low: hsl(0, 0%, 11%);
        --color-base-moderate: hsl(0, 0%, 31%);
        --color-base-high: hsl(0, 0%, 45%);
        --color-base-important: hsl(0, 0%, 57%);
        --color-base-critical: hsl(0, 0%, 71%);
    }
}

Tekst kleuren van Primary tot en met Quarternary

Alhoewel je uiteraard ook High, Important en Critical kunt gebruiken voor teksten, hebben teksten en koppen een eigen set van variabelen:

:root {
    --color-text-primary: hsla(0, 0%, 0%, 0.99);
    --color-text-secondary: hsla(0, 0%, 0%, 0.66);
    --color-text-tertiary: hsla(0, 0%, 0%, 0.55);
    --color-text-quaternary: hsla(0, 0%, 0%, 0.42);
}

@media (prefers-color-scheme: dark) {
    :root {
        --color-text-primary: hsla(0, 0%, 100%, 0.65);
        --color-text-secondary: hsla(0, 0%, 100%, 0.55);
        --color-text-tertiary: hsla(0, 0%, 100%, 0.45);
        --color-text-quaternary: hsla(0, 0%, 100%, 0.34);
    }
}

.content {
    color: var(--color-text-primary);
}

Je ziet dat de opacity (‘a’ staat voor alpha channel) in plaats van de lightness is aangepast, omdat ze zo mooier op een gekleurde achtergrond staan. Ze nemen de achtergrondkleur subtiel mee in hun eigen kleur. Je zou opacity ook kunnen toepassen op de andere kleuren, als de contrastwaardes maar kloppen.

Tekst op moderate, high, important en critical is wel zwart of wit

De default tekstkleuren zij bedoeld voor op grote vlakken die een Ground of Low kleur hebben. Als je een control, zoals een button, met bijvoorbeeld Critical als background color hebt, ontstaan er problemen met de default tekstkleuren. Het contrast is te laag, met als gevolg slechtere leesbaarheid, en je zult zien dat het er niet zo mooi uit ziet. Voor deze gevallen geldt: tekst is standaard wit totdat het contrast lager wordt dan 3, enkel en alleen op dat moment wisselt de tekstkleur naar zwart.

Ben je bekend bent met de WCAG 2.0 eisen, dan weet je dat je eigenlijk een contrastwaarde van 4.5, of zelfs 7, moet ambiëren en zeker geen 3. Ja en nee. Ericka Seastrand heeft in 2019 een mini case study uitgevoerd waarmee zij ontdekte dat zelfs mensen die kleurenblind zijn liever witte tekst dan zwarte tekst in een knop willen, ook al is het contrast veel lager. De resultaten zijn niet zwart-wit, daarom raad ik je zeker aan om haar studie te lezen.

Hernoem je huidige kleuren voordat je Dark Mode toevoegt

Je hoeft Dark Mode niet direct in te zetten. Je kunt zonder veel problemen beginnen met het standaardiseren van je kleurnamen zonder dat je opdrachtgever dat door heeft en gebruik maken van CSS variabelen. Dit doe je stapsgewijs en als je klaar bent, heb je binnen no-time Dark Mode toegevoegd.

Work in progress

Deze uitvoering van Dark Mode staat uiteraard niet in steen gebeiteld. Ik wil je meenemen in de overwegingen die zijn gemaakt en hoe deze versie van Dark Mode tot stand is gekomen. Neem het 1-op-1 over of verbeter je eigen versie hiermee. Mocht je er zelf mee stoeien en verbeteringen hebben, laat het me weten.

Bart van de Biezen

Design Lead bij Incision. M'n achtergrond: Industrieel Ontwerpen en daarna Psychologie aan de Universiteit Twente, afgestudeerd bij Philips op midair pointing voor een nieuwe generatie TV's, Apple Design Award voor CSSEdit, usability onderzoeker bij MetrixLab en blogger.

Je kunt me bereiken via e-mail, Twitter, GitHub of Flickr.