# CSS-variabelenthema: licht, donker en merkkleuren

[Canonical HTML page](https://gradiently.design/nl/guide/css-custom-properties-theming)

Thema’s gaan mis als kleuren worden genoemd naar hoe ze eruitzien in plaats van wat ze doen. Benoem de rollen, wijs ze naar een palet, en lichte modus, donkere modus en een tweede merk worden elk een paar regels.

## The short version

- 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.

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](https://gradiently.design/nl/guide/design-tokens), rechtstreeks in CSS uitgedrukt.

| Laag | Voorbeeld | Wie het leest | Verandert wanneer |
| --- | --- | --- | --- |
| Palet | `--violet-600: #7c3aed` | Alleen de rollaag | Het merk opnieuw wordt ontworpen |
| Rol | `--color-accent: var(--violet-600)` | Elk component | Het thema of de modus verandert |
| Component | `--button-bg: var(--color-accent)` | Eén component | Eén component een lokale afwijking nodig heeft |

De componentlaag is optioneel. Gebruik hem alleen waar één component echt moet afwijken van de rol.

```css
: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); }
```

Componenten noemen nooit violet of inkt. Die ene regel maakt het thema verwisselbaar.

- bg: #faf8f5
- surface: #f1ede6
- text: #14121f
- text-muted: #6b6880
- accent: #6d28d9

De rollen van het lichte thema. Een warme papieren achtergrond in plaats van puur wit laat het violette accent rustiger aanvoelen.

### 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.

```css
: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);
}
```

De donkere waarden staan twee keer: eenmaal voor de systeemvoorkeur en eenmaal voor een expliciete keuze. Een preprocessor-mixin of `light-dark()` haalt de herhaling weg.

- bg: #0d0b16
- surface: #17142a
- text: #f2f0fa
- text-muted: #a5a1bd
- accent: #c4b5fd

De rollen van het donkere thema. Het accent wordt een lichter violet, omdat het violet van de lichte modus te donker is om te lezen op een bijna zwarte ondergrond.

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](https://gradiently.design/nl/guide/dark-mode-design) en [verlopen in de donkere modus](https://gradiently.design/nl/guide/dark-mode-gradients) 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.

```html
<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>
```

Draait vóór de eerste weergave. De schakelknop hoeft dan alleen `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 `light` of `dark`; 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](https://gradiently.design/nl/guide/css-color-mix).

```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); }
```

Verlopen kunnen ook tokens zijn. Sla de hele waarde op in één variabele en de hero verandert mee met het merk.

- Haven-hero: `linear-gradient(150deg, #042f2e 0%, #0f766e 55%, #5eead4 100%)`
- Gloed-hero: `linear-gradient(150deg, #431407 0%, #c2410c 55%, #fdba74 100%)`

De twee hero-tokens uit de code hierboven. Dezelfde structuur, dezelfde hoek, ander merk.

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.

> **Verlopen gaan niet uit zichzelf over** Een verloopvariabele veranderen wisselt de achtergrond direct. Om tussen twee verlopen te animeren registreer je de kleurstops met `@property`, uitgelegd in [CSS @property](https://gradiently.design/nl/guide/css-property-animation).

## 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](https://gradiently.design/nl/guide/how-to-choose-brand-colors).

## FAQ

### 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`.
