# Byg et tema med CSS-variabler: lys, mørk og brandfarver

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

Temaer går galt, når farver navngives efter, hvordan de ser ud, i stedet for hvad de gør. Navngiv rollerne, peg dem mod en palet, så bliver lys tilstand, mørk tilstand og et andet brand til nogle få linjer hver.

## The short version

- Et tema med CSS-variabler virker bedst i to lag: paletvariabler, der rummer rå farver, og semantiske variabler, der beskriver, hvad hver farve bruges til.
- Komponenter bør kun læse semantiske variabler som --color-text eller --color-surface, aldrig paletværdier direkte.
- Mørk tilstand er så et spørgsmål om at pege de semantiske variabler mod andre paletværdier, under en media query eller en data-attribut.
- Funktionen light-dark() og egenskaben color-scheme lader én deklaration rumme begge tilstande i nuværende browsere.
- Et lille inline-script i head, der sætter det gemte tema, før siden males, forhindrer et glimt af det forkerte tema.

Et **tema med CSS-variabler** er et sæt custom properties som `--color-surface` og `--color-text`, som hver komponent læser i stedet for hårdkodede farver. For at ændre temaet ændrer du variablerne, som regel på `:root` eller på en `data-theme`-attribut, og hele brugerfladen følger med. Tricket, der får det til at skalere, er at dele variablerne i to lag: en palet af rå farver og semantiske roller, der peger mod den palet.

## To lag: palet og roller

Paletvariabler navngives efter, hvad de er: `--violet-600`, `--ink-900`. Semantiske variabler navngives efter, hvad de gør: `--color-accent`, `--color-text-muted`. Komponenter læser kun den anden slags. Når mørk tilstand kommer, peger du rollerne om og rører ikke en eneste komponent. Det er den samme idé som [design tokens](https://gradiently.design/da/guide/design-tokens), udtrykt direkte i CSS.

| Lag | Eksempel | Hvem læser det | Ændrer sig, når |
| --- | --- | --- | --- |
| Palet | `--violet-600: #7c3aed` | Kun rollelaget | Brandet redesignes |
| Rolle | `--color-accent: var(--violet-600)` | Hver komponent | Temaet eller tilstanden ændrer sig |
| Komponent | `--button-bg: var(--color-accent)` | Én komponent | En enkelt komponent har brug for en lokal drejning |

Komponentlaget er valgfrit. Brug det kun, hvor én komponent virkelig skal afvige fra rollen.

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

Komponenter nævner aldrig violet eller blæk. Den ene regel er det, der gør temaet udskifteligt.

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

Det lyse tema's roller. En varm papirbund frem for ren hvid får den violette accent til at føles roligere.

### Rollenavne, der overlever et redesign

Et rollenavn bør stadig være sandt, efter farverne ændrer sig. `--light-grey` holder op med at være lys i mørk tilstand, og `--blue` lyver den dag, brandet bliver grønt. Navngiv i stedet jobbet og parringen, så den, der læser stylesheetet næste gang, ved, hvilken farve der hører til hvad.

### Navne, der går i stykker

- `--blue`, `--purple-button`
- `--light-grey-bg`
- `--white-text`
- `--dark-border`

### Navne, der holder

- `--color-accent`, `--color-on-accent`
- `--color-surface`
- `--color-text`
- `--color-border`

## Tilføj mørk tilstand med CSS-variabler

Mørk tilstand peger rollerne om. Respektér styresystemets indstilling som standard, og lad en `data-theme`-attribut tilsidesætte den, når læseren vælger. Sæt også `color-scheme`, så rullebjælker, formularfelter og standardlærredet passer.

```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 mørke værdier står to gange: én gang for systemets præference og én gang for et eksplicit valg. En præprocessor-mixin eller `light-dark()` fjerner gentagelsen.

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

Det mørke tema's roller. Accenten løftes til en lysere violet, fordi den violette fra lys tilstand er for mørk til at kunne læses på en næsten sort bund.

Mørk tilstand er ikke omvendt lys tilstand. Overflader bliver lysere, jo højere de hæver sig, accenter lysnes, og skygger viger for kanter. [Design af mørk tilstand](https://gradiently.design/da/guide/dark-mode-design) og [gradienter i mørk tilstand](https://gradiently.design/da/guide/dark-mode-gradients) dækker den visuelle side.

### Genvejen light-dark()

Nuværende browsere understøtter `light-dark()`, som tager en lys og en mørk værdi og vælger den ene ud fra elementets `color-scheme`. Sæt `color-scheme: light dark` på roden for at følge systemet, eller gennemtving det ene med attributten, så bliver hver rolle til én linje: `--color-bg: light-dark(var(--paper-50), var(--night-950))`. Behold den længere form, hvis du skal understøtte browsere fra før 2024.

## En temaomskifter, der ikke blinker

Hvis det gemte valg anvendes, efter siden er gengivet, ser læsere, der valgte mørk, et hvidt glimt ved hver indlæsning. Sæt attributten i et lille inline-script i `head`, før noget stylesheet maler body.

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

Kører før første maling. Omskifterknappen skal så kun sætte `dataset.theme` og gemme værdien i `localStorage`.

1. **Tilbyd tre valg** Lys, mørk og system. Når attributten fjernes, går styringen tilbage til `prefers-color-scheme`.
2. **Gem kun eksplicitte valg** Gem `light` eller `dark`. Slet nøglen for system, så en senere ændring i styresystemet respekteres.
3. **Undgå overgange ved indlæsning** Hvis farver har overgange, så slå overgangen først til efter første maling, ellers animerer siden fra lys til mørk, mens den indlæses.

## Brandtemaer ud over lys og mørk

De samme to lag håndterer flere brands i én kodebase. Hvert brand leverer sin egen palet og sine egne rolleværdier under en `data-brand`-attribut, og tilstandene virker stadig ovenpå. Med `color-mix()` kan du udlede hover-tilstande og lysere nuancer af én accent pr. brand, som vist i [color-mix() i CSS](https://gradiently.design/da/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); }
```

Gradienter kan også være tokens. Gem hele værdien i én variabel, så ændrer hero-sektionen sig med brandet.

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

De to hero-tokens fra koden ovenfor. Samme struktur, samme vinkel, forskelligt brand.

Fordi custom properties nedarves, behøver et tema ikke dække hele siden. Sæt `data-theme="dark"` på én sektion, for eksempel et reklamebånd eller en footer, og alt indeni læser de mørke roller, mens resten af siden forbliver lys. Skriv den mørke selektor som `[data-theme="dark"]` frem for `:root[data-theme="dark"]`, hvis du vil have det til at virke, og sæt også `color-scheme` på sektionen.

> **Gradienter har ikke overgange af sig selv** At ændre en gradientvariabel bytter baggrunden øjeblikkeligt. For at animere mellem to gradienter skal du registrere farvestoppene med `@property`, som er forklaret i [CSS @property](https://gradiently.design/da/guide/css-property-animation).

## Hold nettet og dine designs på én palet

Et tema i CSS er kun det halve af et brand. De samme farver skal optræde i dine opslag på sociale medier, dine præsentationer og mails. brandkittet i Gradiently rummer dine paletter, logoer og overskrifts- og brødtekstskrifttyper, og Designer anvender det, når den lægger et design ud, så opslaget, du laver mandag, bruger den samme accent som dit site. Er du stadig i gang med at vælge de farver, så start med [sådan vælger du brandfarver](https://gradiently.design/da/guide/how-to-choose-brand-colors).

## FAQ

### Hvordan laver jeg et tema med CSS-variabler?

Definer paletvariabler med rå farver, og så semantiske variabler som `--color-bg`, der peger mod dem. Komponenter læser kun de semantiske, og temaer peger dem om.

### Hvordan tilføjer jeg mørk tilstand med CSS-variabler?

Tilsidesæt de semantiske variabler inde i `@media (prefers-color-scheme: dark)` og under en `[data-theme="dark"]`-selektor til læsere, der vælger det.

### Hvordan forhindrer jeg, at temaet blinker ved indlæsning?

Læs det gemte tema i et lille inline-script i `head`, og sæt attributten, før stylesheetet maler siden.

### Hvad gør light-dark() i CSS?

Den returnerer sin første værdi i lys tilstand og sin anden i mørk tilstand, ud fra elementets `color-scheme`. Den virker i nuværende store browsere.

### Kan jeg gemme en gradient i en CSS-variabel?

Ja. Gem hele gradienten som værdien, og brug den i `background`. For at animere mellem gradienter skal du registrere stoppene med `@property`.
