# CSS-muuttujateema: vaalea, tumma ja brändivärit

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

Teema menee pieleen, kun värit nimetään ulkonäön eikä tehtävän mukaan. Nimeä roolit ja yhdistä ne palettiin, niin vaalea, tumma ja toinen brändi tarvitsevat vain muutaman rivin.

## The short version

- CSS-muuttujateema toimii parhaiten kahdella tasolla: palettimuuttujat sisältävät värit, roolimuuttujat kertovat niiden käyttötarkoituksen.
- Komponentit lukevat vain roolimuuttujia kuten --color-text ja --color-surface, eivät suoraan palettia.
- Tumma tila ohjaa roolimuuttujat toisiin palettiarvoihin mediakyselyllä tai data-attribuutilla.
- light-dark()-funktio ja color-scheme antavat yhden määrittelyn kattaa molemmat tilat nykyisissä selaimissa.
- Pieni head-osan rivinsisäinen skripti asettaa tallennetun teeman ennen piirtämistä ja estää väärän teeman välähdyksen.

**CSS-muuttujateema** koostuu mukautetuista ominaisuuksista kuten `--color-surface` ja `--color-text`, joita komponentit lukevat kovakoodattujen värien sijaan. Muuta muuttujat yleensä `:root`-tasolla tai `data-theme`-attribuutilla, niin käyttöliittymä seuraa mukana. Skaalautuvuuden ratkaisee kaksi tasoa: raakaväripaletti ja siihen viittaavat merkitykselliset roolit.

## Kaksi tasoa: paletti ja roolit

Palettimuuttujat nimetään värin mukaan: `--violet-600`, `--ink-900`. Roolit nimetään tehtävän mukaan: `--color-accent`, `--color-text-muted`. Komponentit lukevat vain rooleja. Tummassa tilassa viittaukset muuttuvat, komponentit säilyvät. Sama idea kuin [suunnittelutokeneissa](https://gradiently.design/fi/guide/design-tokens), suoraan CSS:ssä.

| Taso | Esimerkki | Kuka lukee | Muuttuu kun |
| --- | --- | --- | --- |
| Paletti | `--violet-600: #7c3aed` | Vain roolitaso | Brändi uudistetaan |
| Rooli | `--color-accent: var(--violet-600)` | Jokainen komponentti | Teema tai tila vaihtuu |
| Komponentti | `--button-bg: var(--color-accent)` | Yksi komponentti | Yksittäinen komponentti tarvitsee poikkeuksen |

Komponenttitaso on valinnainen. Käytä vain, kun komponentin pitää poiketa roolista.

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

Komponentit eivät nimeä violettia tai mustetta. Tämä sääntö tekee teemasta vaihdettavan.

- Tausta: #faf8f5
- Pinta: #f1ede6
- Teksti: #14121f
- Hillitty teksti: #6b6880
- Korostus: #6d28d9

Vaalean teeman roolit. Lämmin paperipohja puhtaan valkoisen sijaan rauhoittaa violettia korostusta.

### Nimeä roolit kestämään uudistus

Roolin pitää pysyä totena värin vaihtuessa. `--light-grey` ei ole vaalea tummassa tilassa, ja `--blue` valehtelee brändin muuttuessa vihreäksi. Nimeä tehtävä ja pari, jotta seuraava tyylitiedoston lukija ymmärtää väriyhdistelmän.

### Hajoavat nimet

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

### Kestävät nimet

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

## Tumma tila CSS-muuttujilla

Tumma tila ohjaa roolit uudelleen. Noudata oletuksena käyttöjärjestelmää ja anna käyttäjän `data-theme`-valinnan ohittaa se. Aseta myös `color-scheme`, jotta vierityspalkit, lomakekentät ja oletuspohja sopivat teemaan.

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

Tummat arvot toistuvat järjestelmävalinnalle ja erilliselle valinnalle. Esikäsittelijän mixin tai light-dark() poistaa toiston.

- Tausta: #0d0b16
- Pinta: #17142a
- Teksti: #f2f0fa
- Hillitty teksti: #a5a1bd
- Korostus: #c4b5fd

Tumman teeman roolit. Korostus vaalenee, sillä vaalean tilan violetti on liian tumma lähes mustalla pohjalla.

Tumma tila ei ole käännetty vaalea tila. Kohotetut pinnat vaalenevat, korostukset kevenevät ja varjot vaihtuvat reunaviivoiksi. [Tumman teeman suunnittelu](https://gradiently.design/fi/guide/dark-mode-design) ja [tumman tilan liukuvärit](https://gradiently.design/fi/guide/dark-mode-gradients) käsittelevät ilmettä.

### light-dark()-oikotie

Nykyiset selaimet tukevat `light-dark()`-funktiota, joka valitsee vaalean tai tumman arvon elementin `color-scheme`-asetuksesta. Juuren `color-scheme: light dark` seuraa järjestelmää, attribuutti pakottaa tilan. Rooli tiivistyy yhteen riviin: `--color-bg: light-dark(var(--paper-50), var(--night-950))`. Säilytä pidempi malli ennen vuotta 2024 julkaistuille selaimille.

## Teemavalinta ilman välähdystä

Jos tallennettu valinta asetetaan vasta renderöinnin jälkeen, tumman teeman käyttäjä näkee valkoisen välähdyksen joka latauksessa. Aseta attribuutti pienellä rivinsisäisellä `head`-skriptillä ennen sivun piirtämistä.

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

Suoritetaan ennen ensimmäistä piirtoa. Valintapainike asettaa dataset.theme-arvon ja tallentaa sen localStorageen.

1. **Tarjoa kolme valintaa** Vaalea, tumma ja järjestelmä. Attribuutin poistaminen palauttaa ohjauksen `prefers-color-scheme`-asetukselle.
2. **Tallenna vain erillinen valinta** Tallenna `light` tai `dark`, poista avain järjestelmävalinnalla, jotta myöhempi käyttöjärjestelmän muutos huomioidaan.
3. **Vältä siirtymää latauksessa** Jos värit animoituvat, ota siirtymä käyttöön vasta ensimmäisen piirron jälkeen. Muuten lataus animoi vaaleasta tummaan.

## Bränditeemat vaalean ja tumman lisäksi

Samat kaksi tasoa tukevat monta brändiä samassa koodissa. Brändi tarjoaa paletti- ja rooliarvot `data-brand`-attribuutin alla, ja tilat toimivat sen päällä. `color-mix()` johtaa osoitintilat ja vaalennukset yhdestä korostuksesta per brändi, kuten [CSS color-mix()](https://gradiently.design/fi/guide/css-color-mix) näyttää.

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

Myös liukuväri voi olla token. Tallenna koko arvo muuttujaan, niin pääkuva seuraa brändiä.

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

Yllä olevan koodin kaksi pääkuvatokenia. Sama rakenne ja kulma, eri brändi.

Mukautetut ominaisuudet periytyvät, joten teeman ei tarvitse kattaa koko sivua. Aseta `data-theme="dark"` mainoskaistaan tai alatunnisteeseen. Sen sisältö lukee tummia rooleja muun sivun pysyessä vaaleana. Käytä valitsinta `[data-theme="dark"]` eikä `:root[data-theme="dark"]` ja aseta osioon myös `color-scheme`.

> **Liukuväri ei siirry itsestään** Liukuvärimuuttujan vaihto muuttaa taustan heti. Animoi kahden liukuvärin välillä rekisteröimällä väripisteet `@property`-säännöllä. [CSS @property](https://gradiently.design/fi/guide/css-property-animation) selittää sen.

## Sama paletti verkossa ja suunnitelmissa

CSS-teema on vain puolet brändistä. Samojen värien pitää näkyä somessa, esityksissä ja sähköposteissa. Gradientlyn brändipaketti säilyttää paletit, logot ja otsikko- sekä leipätekstifontit. Designer käyttää niitä asettelussa, joten maanantain julkaisu jakaa sivuston korostusvärin. Jos valinta on kesken, aloita artikkelista [brändivärien valinta](https://gradiently.design/fi/guide/how-to-choose-brand-colors).

## FAQ

### Miten teen teeman CSS-muuttujilla?

Määritä palettimuuttujat raakaväreillä ja niihin viittaavat roolit kuten `--color-bg`. Komponentit lukevat vain rooleja, joita teemat ohjaavat uudelleen.

### Miten lisään tumman tilan CSS-muuttujilla?

Ohita roolimuuttujat `@media (prefers-color-scheme: dark)` -kyselyssä sekä `[data-theme="dark"]`-valitsimessa käyttäjän erillistä valintaa varten.

### Miten estän teeman välähdyksen latauksessa?

Lue tallennettu teema pienellä rivinsisäisellä head-skriptillä ja aseta attribuutti ennen sivun piirtämistä.

### Mitä light-dark() tekee CSS:ssä?

Se palauttaa ensimmäisen arvon vaaleassa ja toisen tummassa tilassa elementin color-scheme-asetuksen mukaan. Nykyiset suuret selaimet tukevat sitä.

### Voinko tallentaa liukuvärin CSS-muuttujaan?

Kyllä. Tallenna koko liukuväri arvoksi ja käytä sitä backgroundissa. Animointia varten rekisteröi väripisteet @property-säännöllä.
