La brukeren velge tema
Fargene følger maskinens innstilling uten at du gjør noe. Vil brukeren overstyre den, er selve byttet ren CSS: fs-theme-control på en radioknapp gjør value til et temavalg. Å få valget til å vare er noe annet, og det er appens oppgave.
Markupen
Section titled “Markupen”Gruppa er et vanlig <fieldset> med radioknapper, altså toggle-gruppa. Klassen står på hver knapp, ikke på gruppa.
<fieldset class="fs-toggle-group"> <legend class="fs-sr-only">Tema</legend> <label class="fs-toggle-group__option"> <input class="fs-theme-control" type="radio" name="tema" value="auto" checked /> Følg systemet </label> <label class="fs-toggle-group__option"> <input class="fs-theme-control" type="radio" name="tema" value="light" /> Lyst </label> <label class="fs-toggle-group__option"> <input class="fs-theme-control" type="radio" name="tema" value="dark" /> Mørkt </label></fieldset>Tre verdier betyr noe: auto, light og dark. «Følg systemet» har ingen temablokk, og det er med vilje: en verdi uten blokk treffer ingenting, så maskinens innstilling gjelder igjen.
light og dark vinner over data-theme på <html>. Serveren kan altså sende det valget den har lagret, mens et klikk likevel slår igjennom før svaret er tilbake.
auto gjør det ikke: regelen for den melder seg eksplisitt ut når <html> bærer et tema, slik at serverens verdi står. Uten det ville :has() slått attributtet på spesifisitet. Skal «Følg systemet» virke med én gang, må det som lagrer valget også fjerne attributtet. Begge oppskriftene under gjør det.
Hva hvert valg gjør
Section titled “Hva hvert valg gjør”Valget styrer to ting, og de er ikke det samme. Fristils farger kommer fra tokenene. Nettleserens egne flater, altså rullefelt, nedtrekkslista i en <select> og kalenderpanelet i et datofelt, styres av CSS-egenskapen color-scheme.
value | Fristils farger | color-scheme |
|---|---|---|
auto | ingen overstyring: mediespørringen, eller data-theme hvis det står der | light dark, eller data-theme sitt |
light | lyse tokens | light |
dark | mørke tokens | dark |
light dark er ikke et tema. Det er en beskjed til nettleseren om at siden fungerer i begge, så den kan tegne sine egne flater etter systeminnstillingen. light og dark låser den til ett.
Du skal ikke sette color-scheme selv når du bruker velgeren. Se siste punkt nederst.
Varigheten
Section titled “Varigheten”CSS kan bytte tema, men ikke huske det. Tre veier, med ulike krav:
| Vei | Krever | Varer over sidelasting |
|---|---|---|
| Bare kontrollen | ingenting | nei |
| Cookie, lest på serveren | en server som rendrer HTML | ja |
localStorage | JavaScript | ja |
Ingen av dem krever et rammeverk.
Cookie lest på serveren
Section titled “Cookie lest på serveren”Dette er veien for en app som rendrer HTML på en server, uansett hvilket språk serveren er skrevet i. Gruppa står i et vanlig skjema, og hele rundturen er tre steg uten en linje JavaScript:
POST /tema tema=dark→ Set-Cookie: tema=dark; Path=/; Max-Age=31536000; SameSite=Lax→ 303 See Other
GET /soknad Cookie: tema=dark→ <html data-theme="dark">Serveren leser headeren og skriver attributtet. I Astro:
---const lagret = Astro.cookies.get("tema")?.valueconst tema = lagret === "light" || lagret === "dark" ? lagret : undefined---
<html lang="nb" data-theme={tema}>Hvitelist de to verdiene. En cookie kommer fra klienten, så den kan inneholde hva som helst: en gammel verdi, eller en noen har skrevet selv. Kopierer du den rett inn, får <html> en data-theme uten blokk, og siden faller til systemtemaet uten at noen av knappene er markert.
auto skal derfor gi ingen data-theme. Skriver du data-theme="auto", treffer ingen temablokk, og du har en verdi i markupen som ikke betyr noe.
Sett samtidig checked på den knappen som svarer til cookien, slik at gruppa viser det lagrede valget.
localStorage på en statisk side
Section titled “localStorage på en statisk side”En helt statisk side har ingen server som kan lese cookien. Da er localStorage den eneste som varer, og skriptet må stå blokkerende i <head>, før siden tegnes:
<!-- I <head>, uten defer, før siden tegnes --><script> const lagret = localStorage.getItem("tema") if (lagret === "light" || lagret === "dark") { document.documentElement.dataset.theme = lagret }</script>Sjekk mot de to verdiene framfor mot «ikke auto». localStorage deles mellom alt på samme opphav, så nøkkelen kan inneholde noe annet enn det du skrev: en gammel verdi, eller en annen app sin. Skriver du den rett inn, får <html> en data-theme uten blokk, og siden faller til systemtemaet uten at noen av knappene er markert.
Står skriptet nederst i <body>, eller har det defer, rekker siden å tegne seg i feil tema først.
Noe må også lagre valget. Dette kan stå hvor som helst etter gruppa:
<script type="module"> const lagret = localStorage.getItem("tema") const valgt = lagret === "light" || lagret === "dark" ? lagret : "auto"
for (const knapp of document.querySelectorAll(".fs-theme-control")) { knapp.checked = knapp.value === valgt
knapp.addEventListener("change", () => { localStorage.setItem("tema", knapp.value) // Attributtet fra skriptet i <head> må bort, ellers står det foran // «Følg systemet», som ikke har noen temablokk å vinne med. delete document.documentElement.dataset.theme }) }</script>Det systemet ikke gjør
Section titled “Det systemet ikke gjør”- Å lagre valget. Kontrollen bytter tema og husker ingenting. Varigheten er appens, gjennom en cookie eller
localStorage. - Å virke inne i en skyggerot.
:has()ser ikke ut av sitt eget tre, så en kontroll i en skyggerot setter ikke tema på siden. Den må stå i det samme treet som<html>. - Å avgjøre mellom to velgere. Står det to grupper på siden, altså to ulike
nameeller to skjemaer, er én knapp avkrysset i hver. Sier de ulike ting, kan ikke CSS vite hvilken som gjelder, og tokenene ogcolor-schemekan ende på hver sin. Bruk én gruppe per side. - Å gi en del av siden sitt eget tema. Kontrollen gjelder hele dokumentet. Skal bare et utsnitt ha sitt eget tema, er det
data-themepå det utsnittet, og det er en temagrense. - Å trenge
color-schemefra deg. Hvert valg setter den selv, «Følg systemet» medregnet. Skriver du den på<html>utenfor et lag, slår regelen@layer fristil, og nettleserens egne flater blir låst til systemet mens brukeren har valgt noe annet.