N1 Nex1on

Mörkt tema utan blinkning: en temaväxlare som minns valet

En knapp för mörkt tema som inte blinkar vitt vid omladdning: ett kort skript i head, light-dark() och color-scheme. All kod, skärmbilder på mobil, surfplatta och dator och en kontroll av att ingen blinkning uppstår.

Du har slagit på ett mörkt tema, och vid varje omladdning blinkar sidan vit en bråkdels sekund innan den blir mörk. Det är det vanligaste felet i en temaväxlare, och det åtgärdas med ett litet skript på rätt ställe. Nedan finns hela receptet: kod du kan kopiera, en förklaring av hur den fungerar och ett sätt att själv kontrollera resultatet.

Det här får du #

  • Som standard följer temat systemets tema (ljust eller mörkt) och fungerar även med JavaScript avstängt.
  • En knapp växlar tema, och valet sparas i webbläsaren.
  • Det sparade valet tillämpas före den första målningen: ingen vit blinkning vid omladdning.
Mörkt tema på en mobil, 390 brett: ett sidhuvud med knappen ”Dark” och ett kort
Mörkt tema på en mobil, 390 brett: ett sidhuvud med knappen ”Dark” och ett kort
Mörkt tema på en surfplatta, 768 brett
Mörkt tema på en surfplatta, 768 brett
Mörkt tema på en dator, 1280 brett: ett centrerat kort, inget överflödigt
Mörkt tema på en dator, 1280 brett: ett centrerat kort, inget överflödigt

Varför sidan blinkar #

Webbläsaren målar upp sidan medan den läser HTML-koden. Om användarens val tillämpas av ett skript i slutet av sidan, eller efter inläsningen, hinner webbläsaren måla den ljusa versionen och målar sedan om den. Valet måste vara känt innan den första pixeln visas, så skriptet måste stå allra först på sidan, i head.

Steg 1. Ange att webbplatsen har båda temana #

Lägg en meta-rad i head och direkt efter den ett litet skript. Det läser det sparade valet och, om det finns ett, skriver det till attributet 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 är kort och väntar inte på att sidan ska läsas in. Blocket try finns där eftersom webbläsaren kastar ett fel i privat läge eller när lagringen är blockerad, och utan det skulle hela sidan bli utan tema.

Steg 2. Beskriv färgerna en gång #

Färgerna för varje tema är ett värdepar i funktionen light-dark(): det första för det ljusa temat, det andra för det mörka. Egenskapen color-scheme väljer rätt värde:
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å länge det inte finns något data-theme gäller color-scheme: light dark: sidan följer systemets tema utan en enda rad JavaScript. När skriptet har skrivit in användarens val tvingar de två sista raderna fram rätt tema.

Steg 3. Lägg till en knapp #

bodyHTML
<button id="theme-toggle" type="button" aria-pressed="false"></button>
En knapp med två lägen är enklare än en med tre (”ljust, mörkt, som systemet”): det är tydligt vad som är valt, och det finns inget tredje alternativ att förklara. Tills personen trycker på den följer webbplatsen ändå 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();
})();
Det här skriptet kan läsas in i slutet av sidan: det behöver inte köras före den första målningen, det sätter bara knappens text och sparar valet.

Kontrollera själv #

Vi testade receptet i en webbläsare på en sida med ett långsamt skript (två sekunder) före slutet, med valet ”dark” sparat i lagringen och ljust systemtema. 0,8 sekunder efter att inläsningen började: med skriptet i head är sidan redan mörk, med skriptet i slutet är den vit och förblir det tills sidan har lästs till slutet. Så här ser det ut för en besökare i det ögonblicket:
Skriptet i slutet av sidan: valet ”dark” är sparat, men sidan är ljus – det är blinkningen
Skriptet i slutet av sidan: valet ”dark” är sparat, men sidan är ljus – det är blinkningen
Skriptet i head: samma ögonblick av inläsningen, sidan är mörk direkt
Skriptet i head: samma ögonblick av inläsningen, sidan är mörk direkt

Resten är också kontrollerat: utan JavaScript följer sidan systemets tema, efter ett tryck på knappen och en omladdning finns valet kvar, och vid 390, 768 och 1280 punkter finns ingen horisontell rullning.
Du kan upprepa testet så här:

  1. Öppna sidan, tryck på knappen och välj det mörka temat.
  2. Slå på nätverksstrypning i utvecklarverktygen och ladda om sidan: bakgrunden är mörk från första bildrutan.
  3. Stäng av JavaScript och töm lagringen: sidan följer systemets tema igen.

Vanliga misstag #

  • Skriptet i slutet av sidan. Blinkningen kommer tillbaka: valet blir känt för sent.
  • Inget color-scheme på :root. Utan det vet light-dark() inte vilket värde som ska användas.
  • Inget try runt localStorage. I privat läge går sidan sönder i vissa webbläsare.
  • Temat bara i JavaScript. Utan skriptet får besökaren inget mörkt tema, även om det är påslaget i systemet. Receptet löser det med color-scheme.
  • Kontrasten är inte kontrollerad. Text och bakgrund bör ge minst 4,5 till 1, tillgänglighetskravet WCAG AA. Kontrollera båda temana, inte bara ett.
Dela: Telegram WhatsApp

Läs också

Diskussion

Kommentarer

Ingen diskussion ännu

Ingen har kommenterat ännu. Bli först.

Anmäl det här inlägget