N1 Nex1on

Mørkt tema uten blink: en bryter som husker valget

En knapp for mørkt tema som ikke blinker hvitt ved ny innlasting: et kort skript i head, light-dark() og color-scheme. All koden, skjermbilder på telefon, nettbrett og datamaskin, og en sjekk av at det ikke blinker.

Du har slått på et mørkt tema, og ved hver ny innlasting blinker siden hvitt et brøkdels sekund før den blir mørk. Dette er den vanligste feilen i en temabryter, og den rettes med ett lite skript på riktig sted. Nedenfor finner du hele oppskriften: kode du kan kopiere, en forklaring på hvordan den virker, og en måte å sjekke resultatet selv på.

Dette får du #

  • Som standard følger temaet systemets tema (lyst eller mørkt) og virker selv med JavaScript slått av.
  • En knapp bytter tema, og valget huskes i nettleseren.
  • Det lagrede valget tas i bruk før første opptegning: ingen hvit blink ved ny innlasting.
Det mørke temaet på en telefon, 390 bred: en topplinje med knappen «Dark» og et kort
Det mørke temaet på en telefon, 390 bred: en topplinje med knappen «Dark» og et kort
Det mørke temaet på et nettbrett, 768 bred
Det mørke temaet på et nettbrett, 768 bred
Det mørke temaet på en datamaskin, 1280 bred: et sentrert kort, ingenting overflødig
Det mørke temaet på en datamaskin, 1280 bred: et sentrert kort, ingenting overflødig

Hvorfor siden blinker #

Nettleseren tegner siden mens den leser HTML-en. Hvis brukerens valg tas i bruk av et skript nederst på siden, eller etter innlasting, rekker nettleseren å tegne den lyse versjonen og tegner den deretter på nytt. Valget må være kjent før den første pikselen vises, så skriptet må stå helt i begynnelsen av siden, i head.

Trinn 1. Fortell at nettstedet har begge temaene #

Sett en meta-linje i head og rett etter den et lite skript. Det leser det lagrede valget og skriver det, hvis det finnes, til attributtet data-theme på rotelementet:
headHTML
<meta name="color-scheme" content="light dark">
<script>
(function () {
  var saved = null;
  try { saved = localStorage.getItem('theme'); } catch (e) {}
  if (saved === 'light' || saved === 'dark') {
    document.documentElement.dataset.theme = saved;
  }
})();
</script>
Skriptet er kort og venter ikke på at siden skal lastes. try-blokken er der fordi nettleseren kaster en feil i privat modus eller når lagring er blokkert, og uten den ville hele siden stått uten tema.

Trinn 2. Beskriv fargene én gang #

Fargene i hvert tema er et verdipar i funksjonen light-dark(): den første for det lyse temaet, den andre for det mørke. Egenskapen color-scheme velger den riktige:
style.cssCSS
:root {
  color-scheme: light dark;
  --bg: light-dark(#ffffff, #14161a);
  --text: light-dark(#1b1f24, #e6e8eb);
  --card: light-dark(#f3f5f8, #1e2228);
  --line: light-dark(#d8dee6, #2c333c);
  --accent: light-dark(#1a56db, #7aa7ff);
}
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }
body { background: var(--bg); color: var(--text); }
Så lenge det ikke finnes noen data-theme, virker color-scheme: light dark: siden tar systemets tema uten en eneste linje JavaScript. Når skriptet har skrevet brukerens valg, tvinger de to siste linjene fram riktig tema.

Trinn 3. Legg til en knapp #

bodyHTML
<button id="theme-toggle" type="button" aria-pressed="false"></button>
En knapp med to tilstander er enklere enn med tre («lyst, mørkt, som i systemet»): det er tydelig hva som er trykket, og det finnes ikke noe tredje valg å forklare. Inntil personen trykker på den, følger nettstedet uansett systemet.
theme.jsJavaScript
(function () {
  var root = document.documentElement;
  var button = document.getElementById('theme-toggle');
  var names = { dark: 'Dark', light: 'Light' };

  function current() {
    return root.dataset.theme ||
      (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
  }
  function show() {
    var now = current();
    button.textContent = names[now];
    button.setAttribute('aria-pressed', now === 'dark');
  }

  button.addEventListener('click', function () {
    var next = current() === 'dark' ? 'light' : 'dark';
    root.dataset.theme = next;
    try { localStorage.setItem('theme', next); } catch (e) {}
    show();
  });
  show();
})();
Dette skriptet kan lastes nederst på siden: det trenger ikke å kjøre før første opptegning, det setter bare tekst på knappen og lagrer valget.

Sjekk det selv #

Vi testet oppskriften i en nettleser på en side med et tregt skript (to sekunder) før slutten, med valget «dark» lagret i lagringen og et lyst systemtema. 0,8 sekunder etter at innlastingen startet: med skriptet i head er siden allerede mørk, med skriptet nederst er den hvit, og den forblir hvit til siden er lest helt ut. Dette er hva en person ser i det øyeblikket:
Skript nederst på siden: valget «dark» er lagret, men siden er lys, dette er blinken
Skript nederst på siden: valget «dark» er lagret, men siden er lys, dette er blinken
Skript i head: samme øyeblikk i innlastingen, siden er mørk med en gang
Skript i head: samme øyeblikk i innlastingen, siden er mørk med en gang

Resten er også sjekket: uten JavaScript tar siden systemets tema, etter et trykk på knappen og ny innlasting blir valget stående, og ved 390, 768 og 1280 punkter er det ingen vannrett rulling.
Du kan gjenta det slik:

  1. Åpne siden, trykk på knappen og velg det mørke temaet.
  2. Slå på nettverksstruping i utviklerverktøyene og last inn siden på nytt: bakgrunnen er mørk fra første bilde.
  3. Slå av JavaScript og tøm lagringen: siden følger systemets tema igjen.

Vanlige feil #

  • Skriptet nederst på siden. Blinken kommer tilbake: valget blir kjent for sent.
  • Ingen color-scheme på :root. Uten den vet ikke light-dark() hvilken verdi den skal ta.
  • Ingen try rundt localStorage. I privat modus går siden i stykker i enkelte nettlesere.
  • Temaet bare i JavaScript. Uten skriptet får den besøkende ikke noe mørkt tema, selv om det er slått på i systemet. Oppskriften løser dette med color-scheme.
  • Kontrasten er ikke sjekket. Tekst og bakgrunn bør gi minst 4,5 til 1, tilgjengelighetskravet WCAG AA. Sjekk begge temaene, ikke bare ett.
Del: Telegram WhatsApp

Les også

Diskusjon

Kommentarer

Ingen diskusjon ennå

Ingen har kommentert ennå. Bli den første.

Rapporter dette innlegget