(Automatische) Dark-/Light-Mode-Umschaltung


Hintergrund:

Moderne Betriebssystem wie Windows, macOS, iOS usw. bieten die Möglichkeit zur Anpassung der Benutzeroberfläche an den persönlichen Geschmack beziehungsweise and die Tages-(Nacht)-Zeit indem sie einen Tag-Modus (english: Day oder Light Mode) mit heller Bedienoberfläche und einen Nacht-Modus (englisch: Night oder Dark Mode) mit dunkler Bedienoberfläche anbieten.

Dies erfordert natürlich auch eine Anpassung der Webseiten. Es würde schließlich keinen Sinn machen in den Dark Mode zu schalten um dann beim Internet Surfen von weißen Webseiten geblendet zu werden.

Um dies zu gewährleisten gibt es verschiedene Ansätze. Ich werde hier drei mögliche Lösungen vorstellen:

  1. Externe Style Sheets (.CSS Dateien) für den jeweiligen Modus
  2. Regeln für beide Modi innerhalb des Webseiten-Codes bzw. mit verlinktem externen Style Sheet
  3. Eine Kombination aus HTML- , CSS-Code und etwas Hilfe von JavaScript

1. Externe Style Sheets (.CSS Dateien) für den jeweiligen Modus

Vorteile:

  • Der Besucher bekommt automatisch den Modus zu sehen, den er auf seinen System aktiviert hat
  • Es ist nur CSS-Basiswissen nötig

Nachteile:

  • Es müssen mehrere CSS-Dateien erstellt werden
  • Der Besucher kann den Modus nicht umschalten

Der Trick besteht darin verschiedene CSS-Dateien für den jeweiligen Modus anzubieten. Der untenstehende Code muß im <head></head> Teil des Web-Codes eingetragen werden.

<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="light.css" media="(prefers-color-scheme: light)">
<link rel="stylesheet" href="dark.css"  media="(prefers-color-scheme: dark)">

Zeile 1: Dies ist der Code, der keine Farbdefinitionen bzw. nur allgemeingültige Regeln enthält.
Zeile 2: Dieser Code enthält die Regeln für den Light-Mode.
Zeile 3: Dieser Code enthält die Regeln für den Dark-Mode.


2. Regeln für beide Modi innerhalb des Webseiten-Codes

Vorteile:

  • Der Besucher bekommt automatisch den Modus zu sehen, den er auf seinen System aktiviert hat
  • Es muss nur eine CSS-Datei erstellt werden

Nachteile:

  • Es ist etwas mehr als nur CSS-Basiswissen nötig
  • Der Besucher kann den Modus nicht umschalten

Um beide Modi zu befriedigen muss der CSS-Code im <head></head> Teil des Web-Codes eingetragen werden.

(Anstatt alles im Webseiten-Code einzutragen, kann natürlich auch eine externe CSS-Datei verlinkt werden, die dann von mehreren Seiten benutzt werden kann.)

<style>
  :root { /* Regeln für den Light Mode */
    --body-bg-color: #fff;
    --body-text-color: #000;
  }
 
  @media (prefers-color-scheme: dark) { /* Regeln für den Dark Mode */
    :root {
      --body-bg-color: #000;
      --body-text-color: #fff;
    }
  }
 
  body { /* Verwendung der Variablen (automatische Anpassung je nach aktivem Modus) */
    background-color: var(--body-bg-color);
    color: var(--body-text-color);
  }
</style>

Im oben gezeigtem Beispiel machen wir Gebrauch von CSS-Variablen. Eine Variable beginnt immer mit zwei Minus-Zeichen (--) und wird dann mittels var(--....) aufgerufen. Sie kann mehrfach verwendet. Im self-html wiki ist die Funktionsweise recht gut erklärt.

Die Bedeutung von :root kannst du hier nachlesen und die Bedeutung von @media hier.


3. Regeln für beide Modi mit der Möglichkeit zum Umschalten der Modi

Diese Variante verwende ich hier auf meiner Webseite.

Vorteile:

  • Der Besucher bekommt automatisch den Modus zu sehen, den er auf seinen System aktiviert hat
  • Der Besucher kann zwischen Light- und Dark-Mode umschalten

Nachteile:

  • Es ist etwas mehr als nur CSS-Basiswissen nötig
  • Es wird JavaScript benötigt. Das bedeutet, dass diese Umschaltfunktion nur bei aktiviertem JavaScript funktioniert. (Die automatische Anpassung an den im System gewählten Modus funktioniert jedoch auch ohne JavaScript!)
  • Es wird zusätzlicher HTML-Code benötigt um den Schalter darzustellen

Damit zwischen den Modi umgeschaltet werden kann brauchen wir drei Teile:

  1. Eine CSS-Datei ähnlich der der in der zweiten Lösung vorgestellen
  2. Etwas JavaScript Code
  3. Etwas HTML-Code
/* Automatisch geladener Stil wenn der Light-Mode aktive ist */
:root {
  --color-mode: "light";
  --body-color: #fff;
  --body-background-color: #000;
}

/* Automatisch geladener Stil wenn der Dark-Mode aktive ist */
@media (prefers-color-scheme: dark) {
  :root {
    --color-mode: "dark";
  }
  :root:not([data-user-color-scheme]) {
    --body-color: #000;
    --body-background-color: #fff;
  }
}
/* Manuell umgeschaltet zu Dark-Mode */
[data-user-color-scheme=dark] {
  --body-color: #000;
  --body-background-color: #fff;
}

/* Verwendung der Variablen (automatische Anpassung je nach aktivem Modus) */
body {
  color: var(--body-color);
  background-color: var(--body-background-color);
}

/* Modus-Umschalter
-------------------------------------------------*/
.theme-switch-wrapper {
  display: flex;
  align-items: center;
  padding-top: .5rem;
}
.theme-switch-wrapper .theme-switch {
  position: relative;
  display: inline-block;
  height: 1rem;
  width: 2rem;
}
.theme-switch-wrapper .theme-switch input {
  display: none;
}
.theme-switch-wrapper .slider {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background-color: #ccc;
  cursor: pointer;
  transition: .4s;
}
.theme-switch-wrapper .slider::before {
  position: absolute;
  left: 0;
  bottom: 0;
  content: "";
  background-color: #0288ff;
  height: 1rem;
  width: 1rem;
  transition: .4s;
}
.theme-switch-wrapper input:checked + .slider {
  background-color: rgba(255, 255, 255, .25);
}
.theme-switch-wrapper input:checked + .slider::before {
  transform: translateX(1rem);
}
.theme-switch-wrapper .slider.round {
  border-radius: 1rem;
}
.theme-switch-wrapper .slider.round::before {
  border-radius: 50%;
}
Im obigen CSS-Code sind mehrere Teile verarbeite:

Zeile 1- 6: Hier werden die Variablen für den Light-Mode gesetzt.
Zeile 8 -17: Hier werden die Variablen für den Dark-Mode gesetzt.
Zeile 18 - 22: Hier werden die Variablen gesetzt, die durch den Umschalter in den Dark-Mode benutzt werden sollen. Im Idealfall sollten sie mit den Variablen von Zeile 14 und 15 identisch sein.
Zeile 30 bis Ende: Hier werden die Eigenschaften für den Schiebeschalter (Modus-Umschalter) festgelegt.

(function() {
  'use strict';
  window.addEventListener('DOMContentLoaded', () => {
    // ---- Dark-Light-Mode Switch -------------------------------------------------------------------------------------
    /**
     * Get value of CSS-Element.
     * @param propKey
     * @returns {string}
     */
    const getCSSCustomProp = (propKey) => {
      let response = getComputedStyle(document.documentElement).getPropertyValue(propKey);
      if (response.length) response = response.replace(/['"]/g, '').trim();
      return response;
    }
    const toggleSwitch = document.getElementById('theme-switch');
    if (getCSSCustomProp('--color-mode') === 'dark') toggleSwitch.checked = true;
    /**
     * Switch dark-light-mode
     * @param e
     */
    function switchTheme(e) {
      e.preventDefault();
      if (e.target.checked) document.documentElement.setAttribute('data-user-color-scheme', 'dark');
      else document.documentElement.setAttribute('data-user-color-scheme', 'light');
    }
    toggleSwitch.addEventListener('change', switchTheme, false);
    // -----------------------------------------------------------------------------------------------------------------
  });
})();

Das JavaScript kann am Ende des <body></body> Blocks eingefügt werden, da es erst ausgeführt wird wenn die Webseite vollständig geladen wurde. Das bedeutet auch, dass es auch als externes Script geladen werden. Zum Beispiel: <script src="switch.js></script>

<form class="theme-switch-wrapper">
  <label class="theme-switch" for="theme-switch">
    <input type="checkbox" id="theme-switch">
    <span class="slider round"></span>
  </label>
</form>

Zum Schluss noch der Schalter. Der Code kann an jeder beliebigen Stelle eingefügt werden, an der der Schalter auf der Webseite erscheinen soll. Allerdings mit einer Einschränkung: Er darf nur einmal pro Seite eingefügt werden, da seine ID im JavaScript abgefragt wird. Wer in jedoch mehrfach einsetzen will, muss die Abfrage im JavaScript in Zeile 15 ändern.