De korte versie
- Een CSS-variabelenthema werkt het best in twee lagen: palet-variabelen met ruwe kleuren en semantische variabelen die beschrijven waar elke kleur voor dient.
- Componenten zouden alleen semantische variabelen moeten lezen, zoals --color-text of --color-surface, nooit rechtstreeks paletwaarden.
- Een donkere modus is dan een kwestie van de semantische variabelen naar andere paletwaarden laten wijzen, onder een mediaquery of een data-attribuut.
- De functie light-dark() en de eigenschap color-scheme laten in huidige browsers één declaratie beide modi bevatten.
- Een klein inline script in de head dat het opgeslagen thema instelt voordat de pagina wordt getekend, voorkomt de flits van het verkeerde thema.
Op deze pagina
Een CSS-variabelenthema is een set custom properties, zoals --color-surface en --color-text, die elk component leest in plaats van vastgecodeerde kleuren. Om het thema te veranderen verander je de variabelen, meestal op :root of op een data-theme-attribuut, en de hele interface volgt. De truc die het schaalbaar maakt is het splitsen van de variabelen in twee lagen: een palet van ruwe kleuren en semantische rollen die naar dat palet wijzen.
Twee lagen: palet en rollen
Paletvariabelen heten naar wat ze zijn: --violet-600, --ink-900. Semantische variabelen heten naar wat ze doen: --color-accent, --color-text-muted. Componenten lezen alleen de tweede soort. Als de donkere modus komt, wijs je de rollen opnieuw toe en blijft elk component onaangeroerd. Het is hetzelfde idee als design tokens, rechtstreeks in CSS uitgedrukt.
--violet-600: #7c3aedWie het leest
Verandert wanneer
--color-accent: var(--violet-600)Wie het leest
Verandert wanneer
Voorbeeld
--button-bg: var(--color-accent)Wie het leest
Verandert wanneer
:root {
/* Palette: raw values, named for what they are */
--paper-50: #faf8f5;
--paper-100: #f1ede6;
--ink-900: #14121f;
--ink-700: #3d3a4f;
--ink-500: #6b6880;
--violet-600: #6d28d9;
--violet-300: #c4b5fd;
--night-950: #0d0b16;
--night-900: #17142a;
--night-800: #221e3a;
/* Roles: what each colour is for (light mode) */
--color-bg: var(--paper-50);
--color-surface: var(--paper-100);
--color-text: var(--ink-900);
--color-text-muted: var(--ink-500);
--color-border: color-mix(in oklab, var(--ink-900) 12%, transparent);
--color-accent: var(--violet-600);
--color-on-accent: #ffffff;
}
body { background: var(--color-bg); color: var(--color-text); }
.card { background: var(--color-surface); border: 1px solid var(--color-border); }
.button { background: var(--color-accent); color: var(--color-on-accent); }Rollen benoemen die een herontwerp overleven
Een rolnaam moet nog waar zijn nadat de kleuren veranderen. --light-grey is in de donkere modus niet meer licht, en --blue liegt de dag dat het merk groen wordt. Benoem in plaats daarvan de taak en de combinatie, zodat wie de stylesheet daarna leest weet welke kleur waarop komt.
Namen die breken
--blue,--purple-button--light-grey-bg--white-text--dark-border
Namen die blijven
--color-accent,--color-on-accent--color-surface--color-text--color-border
Een donkere modus toevoegen met CSS-variabelen
De donkere modus wijst de rollen opnieuw toe. Respecteer standaard de instelling van het besturingssysteem en laat een data-theme-attribuut die overschrijven wanneer de lezer kiest. Stel ook color-scheme in, zodat scrollbalken, formulierbesturing en het standaardcanvas passen.
:root { color-scheme: light; }
/* Follow the system unless the reader picked light */
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
color-scheme: dark;
--color-bg: var(--night-950);
--color-surface: var(--night-900);
--color-text: #f2f0fa;
--color-text-muted: #a5a1bd;
--color-border: color-mix(in oklab, #ffffff 12%, transparent);
--color-accent: var(--violet-300);
--color-on-accent: var(--ink-900);
}
}
/* The reader picked dark explicitly */
:root[data-theme="dark"] {
color-scheme: dark;
--color-bg: var(--night-950);
--color-surface: var(--night-900);
--color-text: #f2f0fa;
--color-text-muted: #a5a1bd;
--color-border: color-mix(in oklab, #ffffff 12%, transparent);
--color-accent: var(--violet-300);
--color-on-accent: var(--ink-900);
}light-dark() haalt de herhaling weg.Een donkere modus is geen omgekeerde lichte modus. Oppervlakken worden lichter naarmate ze hoger liggen, accenten lichten op en schaduwen maken plaats voor randen. Ontwerp voor de donkere modus en verlopen in de donkere modus behandelen de visuele kant.
De light-dark()-snelkoppeling
Huidige browsers ondersteunen light-dark(), dat een lichte en een donkere waarde neemt en er een kiest op basis van het color-scheme van het element. Stel color-scheme: light dark in op de root om het systeem te volgen, of forceer er een met het attribuut, en elke rol wordt één regel: --color-bg: light-dark(var(--paper-50), var(--night-950)). Houd de langere vorm aan als je browsers van vóór 2024 moet ondersteunen.
Een themaschakelaar die niet flitst
Als de opgeslagen keuze wordt toegepast nadat de pagina is getekend, zien lezers die donker kozen bij elke keer laden een witte flits. Stel het attribuut in met een klein inline script in de head, voordat een stylesheet de body tekent.
<head>
<script>
(function () {
var saved = localStorage.getItem("theme");
if (saved === "light" || saved === "dark") {
document.documentElement.dataset.theme = saved;
}
})();
</script>
<link rel="stylesheet" href="/styles.css">
</head>dataset.theme in te stellen en de waarde in localStorage op te slaan.- 1
Bied drie keuzes
Licht, donker en systeem. Het attribuut verwijderen geeft de controle terug aan
prefers-color-scheme. - 2
Sla alleen expliciete keuzes op
Bewaar
lightofdark; verwijder de sleutel voor systeem, zodat een latere wijziging in het besturingssysteem wordt gerespecteerd. - 3
Vermijd overgangen bij het laden
Als kleuren overgaan, schakel de overgang dan pas in na de eerste weergave, anders animeert de pagina tijdens het laden van licht naar donker.
Merkthema’s voorbij licht en donker
Dezelfde twee lagen regelen meerdere merken in één codebase. Elk merk levert zijn eigen palet en rolwaarden onder een data-brand-attribuut, en modi werken er nog steeds bovenop. Met color-mix() kun je hoverstaten en tinten afleiden uit één accent per merk, zoals getoond in color-mix() in CSS.
[data-brand="harbour"] {
--color-accent: #0f766e;
--gradient-hero: linear-gradient(150deg, #042f2e 0%, #0f766e 55%, #5eead4 100%);
}
[data-brand="ember"] {
--color-accent: #c2410c;
--gradient-hero: linear-gradient(150deg, #431407 0%, #c2410c 55%, #fdba74 100%);
}
.button:hover { background: color-mix(in oklab, var(--color-accent), black 12%); }
.hero { background: var(--gradient-hero); }Omdat custom properties worden geërfd, hoeft een thema niet de hele pagina te beslaan. Zet data-theme="dark" op één sectie, zoals een promotieband of een footer, en alles erbinnen leest de donkere rollen terwijl de rest van de pagina licht blijft. Schrijf de donkere selector als [data-theme="dark"] in plaats van :root[data-theme="dark"] als je wilt dat dat werkt, en stel ook color-scheme op de sectie in.
Houd het web en je ontwerpen op één palet
Een thema in CSS is maar de helft van een merk. Dezelfde kleuren horen terug te komen in je social posts, decks en e-mails. De merkkit van Gradiently bevat je paletten, logo’s en kop- en broodlettertypen, en de Designer past hem toe bij het opmaken van een ontwerp, zodat de post die je maandag maakt hetzelfde accent gebruikt als je site. Kies je die kleuren nog, begin dan met merkkleuren kiezen.
Vragen die mensen stellen
Hoe maak ik een thema met CSS-variabelen?
Definieer paletvariabelen met ruwe kleuren en daarna semantische variabelen zoals --color-bg die ernaar wijzen. Componenten lezen alleen de semantische en thema’s wijzen ze opnieuw toe.
Hoe voeg ik een donkere modus toe met CSS-variabelen?
Overschrijf de semantische variabelen binnen @media (prefers-color-scheme: dark) en onder een [data-theme="dark"]-selector voor lezers die dat kiezen.
Hoe voorkom ik dat het thema flitst bij het laden van de pagina?
Lees het opgeslagen thema in een klein inline script in de head en stel het attribuut in voordat de stylesheet de pagina tekent.
Wat doet light-dark() in CSS?
Het geeft zijn eerste waarde in de lichte modus en zijn tweede in de donkere, op basis van het color-scheme van het element. Het werkt in huidige grote browsers.
Kan ik een verloop in een CSS-variabele opslaan?
Ja. Sla het hele verloop als waarde op en gebruik het in background. Om tussen verlopen te animeren registreer je de stops met @property.
Geschreven door Gradiently
Het team achter Gradiently, een ontwerptool rond Marks: levende kleurverlopen die alles wat je ontwerpt op jou laten lijken.
Bekijk ons profiel