Dark Mode für Contao aktivieren

Schritt‑für‑Schritt‑Anleitung für Contao

Abends um elf, das Zimmer dunkel, der Bildschirm strahlend weiß – genau der Moment, in dem Besucher den Dark Mode vermissen. Was als Nischen-Feature für Entwickler begann, wird heute schlicht erwartet: Betriebssysteme, Apps und immer mehr Websites folgen der Systemeinstellung, und wer hell bleibt, blendet – im Wortsinn.

Dabei ist die dunkle Variante mehr als Kosmetik: Sie liest sich bei wenig Licht angenehmer und spart auf OLED-Displays messbar Energie. In Contao lässt sich die Umschaltung zwischen hellem und dunklem Design mit überschaubarem Aufwand selbst bauen – dieser Beitrag zeigt den klassischen Weg mit Schalter und die moderne, fast JavaScript-freie Variante.

1. Vorbereitung: Template-Datei anlegen

Im ersten Schritt wird eine eigene Template-Datei j_dark_mode.html5 im Template-Ordner angelegt. Der folgende JavaScript-Code sorgt dafür, dass das Design je nach Einstellung umschaltet und die gewählte Option gespeichert bleibt:

<script>
const toggleSwitch = document.querySelector('.switch-dark-mode input[type="checkbox"]');

if (localStorage.theme) {
  toggleSwitch.checked = localStorage.theme === 'dark';
}

function switchTheme(e) {
  const theme = e.target.checked ? 'dark' : 'light';
  document.documentElement.setAttribute('data-theme', theme);
  localStorage.theme = theme;
}

toggleSwitch.addEventListener('change', switchTheme);
</script>

Erklärung Zeile für Zeile

  • const toggleSwitch = ...
    Hier wird das Eingabeelement (Checkbox) innerhalb des Elements mit der Klasse .switch-dark-mode gesucht und in der Variablen toggleSwitch gespeichert.
  • if (localStorage.theme) { ... }
    Überprüft, ob im Browser-Speicher (localStorage) bereits ein Wert für das Thema vorhanden ist.
  • toggleSwitch.checked = localStorage.theme === 'dark';
    Setzt den Schalter (Checkbox) auf aktiv, falls das gespeicherte Thema „dark“ lautet. So wird beim erneuten Laden der Seite die letzte Einstellung beibehalten.
  • function switchTheme(e) { ... }
    Definiert eine Funktion, die beim Umschalten ausgeführt wird.
    • const theme = e.target.checked ? 'dark' : 'light';
      Liest den Zustand des Schalters aus: aktiviert = Dark Mode, deaktiviert = Light Mode.
    • document.documentElement.setAttribute('data-theme', theme);
      Setzt ein Attribut data-theme am <html>-Element, das im CSS genutzt werden kann.
    • localStorage.theme = theme;
      Speichert die Einstellung dauerhaft im Browser, damit sie beim nächsten Seitenbesuch erhalten bleibt.
  • toggleSwitch.addEventListener('change', switchTheme);
    Verknüpft den Schalter mit der Funktion switchTheme, sodass beim Umschalten sofort die Darstellung angepasst wird.

2. Modul in Contao anlegen

Anschließend wird im Backend ein neues Modul vom Typ Eigener HTML-Code erstellt. Dieses könnte beispielsweise den Namen Switch-Navigation für Dark Mode tragen. Als HTML wird folgender Code eingefügt:

<div class="switch-dark-mode">
  <label class="switch" for="darkSwitch">
    <input id="darkSwitch" type="checkbox">
    <span class="slider"></span>
  </label>
  <p>Dark Mode</p>
</div>

Dieser Code erzeugt einen Schieberegler (Toggle-Switch), der den Dark Mode aktiviert oder deaktiviert. Das Modul kann im Seitenlayout im Header-Bereich eingebunden werden.

3. Gestaltung mit CSS

Damit der Schalter optisch korrekt angezeigt wird, wird das folgende CSS hinzugefügt:

.switch-dark-mode {
  float: left;
  margin: 4px 0 -4px;
}
.switch-dark-mode p {
  float: right;
  font-size: 12px;
  font-size: 0.75rem;
  padding: 4px 7px;
}
.switch-dark-mode .switch {
  display: inline-block;
  height: 22px;
  position: relative;
  width: 42px;
}
.switch-dark-mode input {
  display: none;
}
.switch-dark-mode .slider {
  bottom: 0;
  cursor: pointer;
  left: 0;
  position: absolute;
  right: 0;
  top: 0;
}
.switch-dark-mode .slider::before {
  bottom: 4px;
  content: "";
  height: 14px;
  left: 4px;
  position: absolute;
  width: 14px;
}
.switch-dark-mode input:checked + .slider::before,
[data-theme="dark"] .switch-dark-mode input + .slider::before {
  -webkit-transform: translateX(21px);
  -ms-transform: translateX(21px);
  transform: translateX(21px);
}

Erklärung des CSS

  • .switch-dark-mode – Positioniert den gesamten Schalterblock.
  • .switch-dark-mode p – Fügt die Beschriftung hinzu und definiert Schriftgröße und Abstände.
  • .switch-dark-mode .switch – Setzt die Größe und relative Position des Schalters.
  • .switch-dark-mode input – Blendet die eigentliche Checkbox aus, da stattdessen ein Slider-Element genutzt wird.
  • .switch-dark-mode .slider – Definiert den klickbaren Bereich des Reglers.
  • .switch-dark-mode .slider::before – Erstellt den kleinen Kreis (Toggle-Button), der verschoben wird.
  • input:checked + .slider::before – Verschiebt den Kreis nach rechts, wenn der Dark Mode aktiviert ist.

4. Automatische Erkennung der Systemeinstellungen

Damit sich die Webseite an den Systemeinstellungen orientiert, wird der JavaScript-Code um eine Erkennung erweitert:

<script>
const toggleSwitch = document.querySelector('.switch-dark-mode input[type="checkbox"]');

function applyTheme(theme) {
  document.documentElement.setAttribute('data-theme', theme);
  toggleSwitch.checked = (theme === 'dark');
  localStorage.theme = theme;
}

if (localStorage.theme) {
  applyTheme(localStorage.theme);
} else {
  const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
  applyTheme(prefersDark ? 'dark' : 'light');
}

toggleSwitch.addEventListener('change', e => {
  const theme = e.target.checked ? 'dark' : 'light';
  applyTheme(theme);
});
</script>

Damit wird bei erstmaligem Besuch automatisch die Systemeinstellung übernommen, solange noch keine eigene Auswahl getroffen wurde.

5. Vorteile des Dark Mode

  • Angenehmeres Lesen bei schlechten Lichtverhältnissen
  • Weniger Blendung durch helle Flächen
  • Energiesparend auf OLED-Displays
  • Moderner, flexibler Look für Webseiten
  • Benutzerfreundlichkeit durch freie Wahl zwischen hellem und dunklem Layout

Der modernere Weg: Systemeinstellung automatisch übernehmen

Update 2026: Der oben gezeigte Weg funktioniert weiterhin. Inzwischen gibt es allerdings eine deutlich schlankere Variante, die ohne umfangreiches JavaScript auskommt, die Systemeinstellung von sich aus respektiert und das lästige Aufblitzen des hellen Layouts beim Laden vermeidet.

Kurz gesagt: Die Farbschemata werden per CSS-Variablen definiert, prefers-color-scheme übernimmt automatisch die Systemeinstellung, und der Schalter setzt lediglich ein Attribut am <html>-Element. JavaScript wird nur noch für die manuelle Umschaltung gebraucht – nicht mehr für die Erkennung.

1. Farbschemata über CSS-Variablen

/* Standard: helles Schema */
:root {
  color-scheme: light dark;   /* Formularfelder, Scrollbalken folgen mit */
  --bg: #ffffff;
  --text: #16181d;
  --link: #c1121f;
}

/* Systemeinstellung dunkel und keine eigene Auswahl getroffen */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0f1420;
    --text: #e8eef7;
    --link: #ff6b6b;
  }
}

/* Manuelle Auswahl schlägt die Systemeinstellung */
:root[data-theme="dark"] {
  --bg: #0f1420;
  --text: #e8eef7;
  --link: #ff6b6b;
}

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

2. Kein Aufblitzen beim Laden

Wird die gespeicherte Auswahl erst nach dem Rendern gelesen, sieht der Besucher für einen Moment das helle Layout. Ein kurzes Skript, direkt im <head> und vor dem Stylesheet, verhindert das:

<script>
  (function () {
    var gespeichert = localStorage.getItem("theme");
    if (gespeichert) {
      document.documentElement.setAttribute("data-theme", gespeichert);
    }
    // ohne gespeicherte Auswahl greift automatisch prefers-color-scheme
  })();
</script>

3. Der Schalter – drei Zustände statt zwei

Sinnvoll sind nicht zwei, sondern drei Zustände: hell, dunkel und „System". Nur so lässt sich eine einmal getroffene Auswahl auch wieder aufgeben:

const html = document.documentElement;

function setzeSchema(wert) {
  if (wert === "system") {
    html.removeAttribute("data-theme");
    localStorage.removeItem("theme");
  } else {
    html.setAttribute("data-theme", wert);   // "light" oder "dark"
    localStorage.setItem("theme", wert);
  }
}

// Systemwechsel im laufenden Betrieb übernehmen,
// solange keine eigene Auswahl aktiv ist
window.matchMedia("(prefers-color-scheme: dark)")
  .addEventListener("change", () => {
    if (!localStorage.getItem("theme")) {
      html.removeAttribute("data-theme");
    }
  });

4. Bilder und Kontraste nicht vergessen

  • Logos mit dunkler Schrift verschwinden im Dark Mode – <picture> mit media="(prefers-color-scheme: dark)" liefert die passende Variante aus.
  • Auch im dunklen Schema gilt das Kontrastverhältnis von mindestens 4,5 : 1. Reines Weiß auf Schwarz ist dabei zu hart – ein leicht gedämpftes Hellgrau liest sich besser.
  • Der Schalter selbst braucht eine Beschriftung und muss per Tastatur bedienbar sein; ein reiner Icon-Toggle ohne aria-label fällt bei jedem Barrierefreiheitstest durch.

Der Aufwand für diese Variante liegt bei etwa einer Stunde – und sie hat den angenehmen Nebeneffekt, dass Besucher, die ihr Gerät ohnehin dunkel eingestellt haben, gar nichts mehr tun müssen.

Fazit

Für den Dark Mode gibt es keine Ausrede mehr: Ein Template, ein Modul, etwas CSS – mehr braucht die klassische Variante nicht. Und die moderne Fassung aus dem Update ist sogar noch genügsamer: CSS-Variablen definieren die Schemata, prefers-color-scheme erkennt die Systemeinstellung von selbst, ein Mini-Skript im Head verhindert das Aufblitzen beim Laden.

Der eigentliche Gewinn zeigt sich bei den Besuchern, die nichts davon merken: Wer sein Gerät dunkel eingestellt hat, bekommt die Website dunkel – ohne Klick, ohne Schalter, ohne Nachdenken. Genau so fühlt sich gute Technik an.

Zurück zur Blog-Übersicht