BlogWebentwicklung

Dark Mode mit light-dark() und color-scheme umsetzen

light-dark() nimmt zwei Farben entgegen und wählt selbstständig die passende aus, je nach aktivem Farbschema. Zusammen mit color-scheme ersetzt die Funktion doppelte Variablensätze und ganze prefers-color-scheme-Media-Queries.

von Oli Feiler · 21. Juli 2026

Dark Mode beginnt in den meisten Projekten mit einer Verdopplung: ein vollständiger Satz Farbvariablen für das helle Theme, ein zweiter für das dunkle, definiert in einer eigenen prefers-color-scheme-Media-Query oder unter einer .dark-Klasse noch einmal komplett wiederholt. light-dark() legt beide Werte stattdessen direkt nebeneinander, an genau der Stelle, an der die Farbe gebraucht wird.

Zwei Farben in einer Funktion

light-dark() nimmt zwei Farbwerte entgegen. Der Browser gibt den ersten zurück, wenn Light Mode aktiv ist oder keine Präferenz vorliegt, den zweiten bei Dark Mode. Damit das funktioniert, muss irgendwo im Dokument color-scheme gesetzt sein und beide Schemata einschließen.

            :root {
  color-scheme: light dark;
}

body {
  background: light-dark(#ffffff, #111827);
  color: light-dark(#111827, #f3f4f6);
}
        

Kein doppelter Variablenblock, keine Media-Query. Hell- und Dunkelwert stehen in derselben Zeile, an derselben Stelle im Code, an der auch die Farbe verwendet wird.

color-scheme ist die Voraussetzung

color-scheme: light dark erlaubt beide Modi, je nachdem, was Betriebssystem oder Nutzende einstellen. Ohne diese Deklaration bleibt light-dark() wirkungslos und liefert immer den ersten Wert.

Schon color-scheme allein bringt einen Effekt, den man leicht übersieht: Checkboxen, Radio-Buttons, Scrollbalken und Datumsfelder übernehmen automatisch das passende Systemtheme, ganz ohne eigene Farbdefinition. Der Browser kennt seine eigenen Bedienelemente bereits in beiden Varianten.

Ein manueller Umschalter

Die automatische Kopplung an die Systemeinstellung reicht nicht, sobald Nutzende das Theme unabhängig vom Betriebssystem selbst wählen sollen. Der Haken dabei: Eine Klasse wie .theme-dark auf <html> zu setzen genügt allein nicht. Ohne ein eigenes color-scheme auf genau dieser Klasse bleibt der Browser bei der Systemeinstellung, unabhängig davon, welche Klasse gerade aktiv ist.

            :root {
  color-scheme: light dark; /* folgt automatisch dem System */
}

html.theme-light {
  color-scheme: light; /* erzwingt Light, unabhängig vom System */
}

html.theme-dark {
  color-scheme: dark; /* erzwingt Dark, unabhängig vom System */
}
        

Den eigentlichen Umschalter übernimmt eine kleine Funktion, die die Klasse setzt und die Wahl speichert.

            const root = document.documentElement;

function setTheme(mode) {
  root.classList.remove('theme-light', 'theme-dark');
  if (mode !== 'system') {
    root.classList.add(`theme-${mode}`);
  }
  localStorage.setItem('theme', mode);
}

// Beim Laden: gespeicherte Präferenz übernehmen, sonst dem System folgen
setTheme(localStorage.getItem('theme') || 'system');
        

Funktioniert light-dark() auch mit color-mix() und var()?

Ja, beide Fälle sind unproblematisch. light-dark() verlangt an beiden Stellen nur einen gültigen <color>-Wert, und sowohl eine Custom Property als auch das Ergebnis von color-mix() liefern genau das.

            :root {
  color-scheme: light dark;
  --brand-light: #22d3ee;
  --brand-dark: #0e7490;
}

.button {
  background: light-dark(var(--brand-light), var(--brand-dark));
}
        

Auch verschachtelt funktioniert es:

            .button {
  background: light-dark(
    color-mix(in oklch, #22d3ee, white 20%),
    color-mix(in oklch, #0e7490, black 20%)
  );
}
        

Für beide Kombinationen gibt es keine dokumentierten Einschränkungen. Da color-mix() und light-dark() beide vergleichsweise jung sind, lohnt sich vor dem produktiven Einsatz trotzdem ein kurzer Test in den Zielbrowsern.

Und mit Gradients?

Seit einer Spezifikations-Erweiterung nimmt light-dark() neben zwei Farben auch zwei <image>-Werte entgegen, und ein Gradient zählt in CSS als Bild. Auf Eigenschaften wie background-image funktioniert damit derselbe Umschaltmechanismus wie bei Farben, nur eben mit ganzen Verläufen.

            .blob {
  background-image: light-dark(
    linear-gradient(135deg, #22d3ee, #6366f1),
    linear-gradient(135deg, #0e7490, #312e81)
  );
}
        

Eine feste Regel dabei: Ein einzelner light-dark()-Aufruf nimmt entweder zwei Farben oder zwei Bilder entgegen, nie gemischt. Der Grund liegt im CSS-Parser, der den Werttyp schon beim Parsen kennen muss. Deshalb funktioniert der Bild-Aufruf auch nur an Stellen, die <image> erwarten, etwa background-image oder mask-image, nicht an der Kurzschreibweise background und nicht bei color.

Der Support ist frisch: Chrome 150 und Firefox 150 unterstützen die Bild-Variante seit Mitte Juni 2026, Safari bislang nicht (WebKit-Bug 309689 ist offen). Für den produktiven Einsatz empfiehlt sich eine Absicherung per Feature-Query.

            @supports (background-image: light-dark(none, none)) {
  /* moderner Bildwechsel */
}
        

Die Farb-Variante von light-dark() gilt bereits seit Mai 2024 als „Newly Available“ (Chrome 123, Firefox 120, Safari 17.5) und erreicht „Widely Available“ voraussichtlich im November 2026. Die Bild-Variante steht also noch am Anfang dieses Reifeprozesses. Damit deckt eine einzige Funktion inzwischen Text- und Hintergrundfarben, Verläufe und Bilder mit demselben Prinzip ab.

Zusammenfassung

TechnikZweck
color-scheme: light dark auf :rootSystemtheme aktivieren, native Bedienelemente automatisch anpassen
light-dark(hell, dunkel)zwei Farbwerte an einer Stelle definieren, statt zwei Variablensätzen
color-scheme: light / dark auf einer Umschalt-Klassemanuelle Auswahl erzwingen, unabhängig vom System
kleine JS-Funktion mit localStorageWahl der Nutzenden merken und beim nächsten Besuch anwenden
light-dark() mit var() oder verschachteltem color-mix()berechnete oder wiederverwendete Farbwerte pro Theme, ohne Einschränkung
light-dark() mit zwei <image>-Werten (z. B. Gradients)ganze Verläufe oder Bilder je Theme wechseln (Chrome/Firefox 150, noch nicht Safari)

Glossar

light-dark()

CSS-Farbfunktion, die zwei Farbwerte entgegennimmt und je nach aktivem Farbschema den passenden zurückgibt. Benötigt ein aktives color-scheme auf demselben Element oder einem Vorfahren.

color-scheme

CSS-Eigenschaft, die festlegt, welche Farbschemata ein Element unterstützt. Steuert sowohl light-dark() als auch das Erscheinungsbild nativer Formularelemente und Scrollbalken.

prefers-color-scheme

Media Feature, das die Systempräferenz für Light oder Dark Mode abfragt. Älterer, media-query-basierter Ansatz, weiterhin nötig für Werte jenseits von Farben, etwa Bilder.

Baseline

Klassifizierung der Web-Platform-Initiative für den Reifegrad einer Web-Technologie über alle großen Browser-Engines hinweg, von „Newly Available“ bis „Widely Available“.

Verwandte Snippets

 alt=

Themes, die sich von selbst pflegen.

Wir entwickeln Farbsysteme, die Light und Dark Mode aus denselben Werten ableiten, technisch sauber und ohne doppelte Pflege über zwei Variablensätze.
Oli Feiler, UI-Designer