aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Frontend & UI

CSS Dark Mode: prefers-color-scheme und Toggle-Anleitung

Ein solides CSS Dark Mode-Setup besteht aus drei Teilen: CSS-Variablen, die deine Farben von einer einzigen Stelle aus verwalten, der Media Query prefers-color-scheme, die die Systemeinstellung des Nutzers ausliest, und einem kleinen Toggle, mit dem jeder das Theme von Hand wechseln kann. Verbindest du diese drei richtig, wird der Dark Mode konsistent, dauerhaft und frei von dem Flackern (Flicker), das so viele Seiten beim Laden plagt. In dieser Anleitung baue ich ihn Schritt für Schritt mit echtem, lauffähigem Code auf.

Farben in CSS-Variablen auslagern

Die Grundidee eines dunklen Themes ist diese: Farbwerte tauschen zu können, ohne irgendetwas in deinem HTML anzufassen. Der sauberste Weg dahin ist, jede Farbe über CSS Custom Properties zu verwalten, die du auf :root deklarierst. So verweisen deine Komponenten auf abstrakte Namen, und der eigentliche Farbwert liegt an genau einer Stelle.

:root {
  --bg: #ffffff;
  --text: #1a1a1a;
  --muted: #5a5a5a;
  --surface: #f4f4f5;
  --accent: #2563eb;
}

body {
  background: var(--bg);
  color: var(--text);
}

.card {
  background: var(--surface);
  border: 1px solid #e4e4e7;
}

Das Theme zu wechseln, ist jetzt so einfach wie das Aktualisieren des Werts dieser fünf Variablen. Du musst nie wieder Hunderte von Regeln wie .card oder body einzeln von Hand bearbeiten.

Die Systemeinstellung mit prefers-color-scheme lesen

Die meisten Nutzer haben bereits auf Betriebssystemebene ein Theme gewählt. Die Media Query prefers-color-scheme erfasst genau diese Einstellung. Deine Seite öffnet sich dann passend zur Geräteeinstellung, ohne dass irgendwo geklickt wird:

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f0f10;
    --text: #ededed;
    --muted: #a1a1aa;
    --surface: #1a1a1d;
    --accent: #60a5fa;
  }
}

Ein paar Punkte zum Beachten. Verwende im Dark Mode ein sehr dunkles Grau (wie #0f0f10) statt reinem Schwarz (#000); weißer Text auf reinem Schwarz ermüdet die Augen und erzeugt einen Unschärfeeffekt namens „Halation". Halte ebenso deinen Text leicht gebrochen weiß statt reinweiß. Außerdem solltest du Akzentfarben vor dunklem Hintergrund etwas aufhellen; ein tiefes Blau, das im Light Mode großartig aussieht, wirkt im Dunkeln blass.

Einen manuellen Toggle hinzufügen

Die Systemeinstellung ist ein guter Anfang, aber Nutzer wollen das Theme immer auch selbst wählen. Dafür schreiben wir die Einstellung als data-theme-Attribut auf das html-Element und bauen das CSS darum herum. Strukturiere das CSS zunächst für drei Zustände: den Standard (hell), das systemgesteuerte Dunkel und das manuell gewählte Dunkel.

/* Wenn der Nutzer manuell dunkel gewählt hat */
:root[data-theme="dark"] {
  --bg: #0f0f10;
  --text: #ededed;
  --muted: #a1a1aa;
  --surface: #1a1a1d;
  --accent: #60a5fa;
}

/* Wenn der Nutzer manuell hell gewählt hat, auch wenn das System dunkel ist */
:root[data-theme="light"] {
  --bg: #ffffff;
  --text: #1a1a1a;
}

Die Feinheit dabei: Fehlt data-theme, behält prefers-color-scheme die Kontrolle; sobald der Nutzer eine Wahl getroffen hat, überschreibt diese das System. Auf der JavaScript-Seite geht es nur darum, dieses Attribut umzuschalten und die Wahl zu merken:

const btn = document.querySelector("#theme-toggle");

btn.addEventListener("click", () => {
  const root = document.documentElement;
  const current = root.getAttribute("data-theme");
  // Das aktuell sichtbare Theme ermitteln
  const isDark = current
    ? current === "dark"
    : matchMedia("(prefers-color-scheme: dark)").matches;
  const next = isDark ? "light" : "dark";
  root.setAttribute("data-theme", next);
  localStorage.setItem("theme", next);
});

Das Flackern (FOUC) verhindern

Das ist der mit Abstand häufigste Fehler: die Theme-Einstellung erst anzuwenden, nachdem die Seite vollständig geladen ist. In diesem Fall sieht ein Nutzer, der den Dark Mode bevorzugt, für einige Millisekunden einen weißen Bildschirm — das nennt man FOUC (Flash of Unstyled Content) bzw. Theme-Flicker. Die Lösung ist, die in localStorage gespeicherte Einstellung so früh wie möglich anzuwenden, im <head> der Seite, bevor überhaupt CSS gezeichnet wird.

<head>
  <script>
    (function () {
      const saved = localStorage.getItem("theme");
      if (saved) {
        document.documentElement.setAttribute("data-theme", saved);
      }
    })();
  </script>
  <link rel="stylesheet" href="styles.css">
</head>

Da dieses winzige Inline-Skript vor dem Rendern läuft, wird der Bildschirm nie mit der falschen Farbe gezeichnet. Platziere das Skript immer vor dem Stylesheet und direkt im HTML; verschiebst du es in eine externe Datei, bringt die Netzwerklatenz das Flackern sofort zurück.

Übergänge sanft gestalten — aber clever

Eine sanfte Farbüberblendung beim Theme-Wechsel sieht schön aus, doch man muss vorsichtig sein. Wendest du transition auf alles an, kann es beim allerersten Seitenaufbau zu einer unerwünschten Animation kommen. Ein praktischer Ansatz ist, den Übergang nur im Moment des Umschaltens zu aktivieren: JavaScript fügt eine temporäre Klasse hinzu und entfernt sie nach einem doppelten requestAnimationFrame.

:root.theme-transition,
:root.theme-transition * {
  transition: background-color .2s ease, color .2s ease;
}

Vergiss aus Gründen der Barrierefreiheit die Einstellung prefers-reduced-motion nicht; deaktiviere Übergänge für Nutzer, die weniger Bewegung wünschen. Füge dem Toggle-Button außerdem ein aria-label hinzu, damit Screenreader seine Funktion erklären können.

Testen und Feinschliff

  • Kontrast: Halte im Dark Mode den Text/Hintergrund-Kontrast auf WCAG-AA-Niveau (mindestens 4,5:1). Das Kontrast-Werkzeug der Browser-DevTools zeigt das sofort an.
  • Bilder: Transparente PNG-Logos können auf dunklem Hintergrund verschwinden; liefere bei Bedarf eine separate Variante für den Dark Mode aus.
  • Schatten: Box-Schatten sind im Dark Mode nahezu unsichtbar. Vermittle Tiefe mit etwas helleren Oberflächenfarben statt mit Schatten.
  • theme-color: Passe auf Mobilgeräten die Farbe der Browserleiste mit <meta name="theme-color"> an das Theme an.

Häufige Fragen

Funktioniert prefers-color-scheme in allen Browsern?

Ja — jeder aktuelle große Browser (Chrome, Firefox, Safari, Edge) unterstützt es seit Jahren. In einem sehr alten Browser wird die Media Query einfach ignoriert, und der Nutzer sieht das Standard-Theme (hell); es funktioniert also sicher als Progressive Enhancement.

Warum die Einstellung in localStorage statt in einem Cookie speichern?

Das Theme ist eine rein clientseitige visuelle Einstellung; sie muss nicht an den Server gesendet werden. localStorage wird synchron gelesen, sodass das Inline-Skript in deinem <head> sie sofort abrufen und Flicker verhindern kann. Möchtest du ein serverseitig gerendertes Theme, ist ein Cookie sinnvoller, da es an die Anfrage angehängt wird.

Reicht prefers-color-scheme nicht — ist ein Toggle wirklich nötig?

Streng genommen nicht, aber empfehlenswert. Manche Nutzer stellen ihr Gerät auf Dunkel, möchten bestimmte Seiten aber hell lesen (oder umgekehrt). Ein manueller Toggle gibt ihnen diese Kontrolle und erhöht die Zufriedenheit.

Ein sauberes, flickerfreies dunkles Theme sieht edel aus und schont gleichzeitig die Augen deiner Nutzer. Wenn du Dark Mode auf deiner Seite einrichten, ein bestehendes Farbsystem auf Variablen umstellen oder deine Theme-Übergänge reparieren möchtest, melde dich bei mir — setzen wir es gemeinsam schnell um.

Bu kategorideki tüm yazılar →

Devamı için