# Tema med CSS-variabler: lyst, mørkt og merkevarefarger

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

Temaer går galt når farger navngis etter utseende fremfor oppgave. Navngi rollene og koble dem til en palett. Da tar lys modus, mørk modus og en ny merkevare bare noen få linjer hver.

## The short version

- Et tema med CSS-variabler fungerer best i to lag: palettvariabler med råfarger og semantiske variabler som beskriver hva hver farge brukes til.
- Komponenter bør bare lese semantiske variabler som --color-text eller --color-surface, aldri palettverdier direkte.
- Mørk modus handler da om å peke de semantiske variablene mot andre palettverdier, i en mediespørring eller et dataattributt.
- Funksjonen light-dark() og egenskapen color-scheme lar én deklarasjon romme begge moduser i nåværende nettlesere.
- Et lite innebygd skript i head som setter det lagrede temaet før siden tegnes, hindrer et blink av feil tema.

Et **tema med CSS-variabler** er et sett egendefinerte egenskaper, som `--color-surface` og `--color-text`, som alle komponenter leser fremfor hardkodede farger. For å endre temaet endrer du variablene, vanligvis på `:root` eller et `data-theme`-attributt, og hele grensesnittet følger. Grepet som gjør dette skalerbart, er å dele variablene i to lag: en palett med råfarger og semantiske roller som peker på den.

## To lag: palett og roller

Palettvariabler navngis etter hva de er: `--violet-600`, `--ink-900`. Semantiske variabler navngis etter hva de gjør: `--color-accent`, `--color-text-muted`. Komponenter leser bare den andre typen. Når mørk modus kommer, kobler du om rollene og lar komponentene være urørt. Det er samme idé som [designtokens](https://gradiently.design/nb/guide/design-tokens), uttrykt direkte i CSS.

| Lag | Eksempel | Hvem leser det | Endres når |
| --- | --- | --- | --- |
| Palett | `--violet-600: #7c3aed` | Bare rollelaget | Merkevaren får nytt design |
| Rolle | `--color-accent: var(--violet-600)` | Alle komponenter | Temaet eller modusen endres |
| Komponent | `--button-bg: var(--color-accent)` | Én komponent | Én komponent trenger en lokal variant |

Komponentlaget er valgfritt. Bruk det bare der én komponent faktisk trenger å avvike 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 nevner aldri fiolett eller blekk. Denne ene regelen gjør temaet utskiftbart.

- bakgrunn: #faf8f5
- flate: #f1ede6
- tekst: #14121f
- dempet tekst: #6b6880
- aksent: #6d28d9

Rollene i det lyse temaet. En varm papirbunn fremfor rent hvitt får den fiolette aksenten til å virke roligere.

### Rollenavn som tåler nytt design

Et rollenavn bør fortsatt være sant etter at fargene endres. `--light-grey` slutter å være lys i mørk modus, og `--blue` lyver den dagen merkevaren blir grønn. Navngi heller oppgaven og fargeparet, så neste person som leser stilarket, vet hvilken farge som skal på hvilken.

### Navn som svikter

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

### Navn som varer

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

## Legg til mørk modus med CSS-variabler

Mørk modus kobler om rollene. Respekter operativsystemets innstilling som standard, og la et `data-theme`-attributt overstyre når leseren velger. Sett også `color-scheme`, så rullefelt, skjemakontroller og standardlerretet 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 verdiene vises to ganger: én for systemvalget og én for et eksplisitt valg. En preprosessor-mixin eller `light-dark()` fjerner gjentakelsen.

- bakgrunn: #0d0b16
- flate: #17142a
- tekst: #f2f0fa
- dempet tekst: #a5a1bd
- aksent: #c4b5fd

Rollene i det mørke temaet. Aksenten blir lysere fiolett, fordi fiolettfargen fra lys modus er for mørk å lese på en nesten svart bunn.

Mørk modus er ikke invertert lys modus. Flater blir lysere jo høyere de ligger, aksenter lysner, og skygger erstattes av kanter. [Design for mørk modus](https://gradiently.design/nb/guide/dark-mode-design) og [gradienter for mørk modus](https://gradiently.design/nb/guide/dark-mode-gradients) dekker det visuelle.

### Snarveien light-dark()

Nåværende nettlesere støtter `light-dark()`, som tar en lys og en mørk verdi og velger ut fra elementets `color-scheme`. Sett `color-scheme: light dark` på rotelementet for å følge systemet, eller tving én modus med attributtet. Hver rolle blir da én linje: `--color-bg: light-dark(var(--paper-50), var(--night-950))`. Behold den lengre formen hvis du må støtte nettlesere fra før 2024.

## En temabryter som ikke blinker

Hvis det lagrede valget brukes etter at siden er rendret, ser lesere som valgte mørkt, et hvitt blink ved hver lasting. Sett attributtet i et lite innebygd skript i `head`, før noe stilark tegner innholdet.

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

Kjører før første tegning. Temaknappen trenger deretter bare å sette `dataset.theme` og lagre verdien i `localStorage`.

1. **Tilby tre valg** Lyst, mørkt og system. Fjerner du attributtet, får `prefers-color-scheme` kontrollen tilbake.
2. **Lagre bare eksplisitte valg** Lagre `light` eller `dark`. Slett nøkkelen for system, så en senere endring i operativsystemet respekteres.
3. **Unngå overganger ved lasting** Hvis farger har overganger, aktiver dem først etter første tegning. Ellers animeres siden fra lyst til mørkt under lasting.

## Merkevaretemaer utover lyst og mørkt

De samme to lagene håndterer flere merkevarer i én kodebase. Hver merkevare gir sin egen palett og rolleverdier under et `data-brand`-attributt, og modusene fungerer fortsatt oppå. Med `color-mix()` kan du avlede pekertilstander og lyse nyanser fra én aksent per merkevare, som vist i [color-mix() i CSS](https://gradiently.design/nb/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. Lagre hele verdien i én variabel, så endres toppfeltet med merkevaren.

- Havnetoppfelt: `linear-gradient(150deg, #042f2e 0%, #0f766e 55%, #5eead4 100%)`
- Glødetoppfelt: `linear-gradient(150deg, #431407 0%, #c2410c 55%, #fdba74 100%)`

De to toppfelttokenene fra koden over. Samme struktur, samme vinkel, ulik merkevare.

Fordi egendefinerte egenskaper arves, trenger ikke et tema å dekke hele siden. Sett `data-theme="dark"` på én seksjon, som et kampanjefelt eller en bunntekst. Alt inni leser de mørke rollene, mens resten av siden er lys. Skriv den mørke selektoren som `[data-theme="dark"]` fremfor `:root[data-theme="dark"]` hvis du vil at dette skal virke, og sett også `color-scheme` på seksjonen.

> **Gradienter får ikke overganger av seg selv** Å endre en gradientvariabel bytter bakgrunnen umiddelbart. For å animere mellom to gradienter registrerer du fargestoppene med `@property`, forklart i [CSS @property](https://gradiently.design/nb/guide/css-property-animation).

## Hold nettsiden og designene på én palett

Et tema i CSS er bare halve merkevaren. Samme farger bør vises i sosiale innlegg, presentasjoner og e-poster. Merkevarepakken i Gradiently rommer paletter, logoer og overskrifts- og brødtekstskrifter. Designer bruker den når et design settes opp, så innlegget du lager mandag, får samme aksent som nettsiden. Hvis du fortsatt velger farger, start med [slik velger du merkevarefarger](https://gradiently.design/nb/guide/how-to-choose-brand-colors).

## FAQ

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

Definer palettvariabler med råfarger og deretter semantiske variabler som `--color-bg` som peker på dem. Komponenter leser bare de semantiske, og temaer kobler dem om.

### Hvordan legger jeg til mørk modus med CSS-variabler?

Overstyr de semantiske variablene inni `@media (prefers-color-scheme: dark)` og under en `[data-theme="dark"]`-selektor for lesere som velger det.

### Hvordan hindrer jeg at temaet blinker ved sidelasting?

Les det lagrede temaet i et lite innebygd skript i `head` og sett attributtet før stilarket tegner siden.

### Hva gjør light-dark() i CSS?

Den returnerer første verdi i lys modus og andre i mørk modus, basert på elementets `color-scheme`. Den virker i nåværende store nettlesere.

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

Ja. Lagre hele gradienten som verdi og bruk den i `background`. For å animere mellom gradienter registrerer du stoppene med `@property`.
