Wie Du einen Dark-Mode mit Tailwind umsetzt

Seit kurzem verfügt Aureola über einen Dark-Mode. Über die kleine Sonne rechts oben im Header kannst Du zwischen der hellen und dunklen Variante der Seite hin und her schalten. Dein Browser merkt sich Deine Auswahl. Wie das genau funktioniert und wie Du das selber in Tailwind umsetzen kannst, erkläre ich Dir in diesem Artikel.

Einen Dark-Mode mit Tailwind umzusetzen ist grundsätzlich nicht schwierig. Je nachdem wie komplex Dein Design ist, kann es aber in Arbeit ausarten. Es gibt hier zwei mögliche Herangehensweisen. Entweder Du reagierst mit einer Media-Query direkt im CSS auf das bevorzugte Farbschema das Benutzers. Oder aber Du liest das Farbschema via Javascript aus und hast zusätzlich die Möglichkeit einen Umschalt-Button zu implementieren. Ich werde Dir hier beide Möglichkeiten vorstellen und Du kannst selbst entscheiden, welche für Dich besser passt.

Farben als Variablen definieren

Bei Tailwind hast Du die Möglichkeit die Farben Deines Designs über die Konfiguration anzupassen (siehe tailwind.config.js). Hier kannst Du entweder Farben ergänzen oder das Farbschema gleich ganz überschreiben. Farben kannst Du zum Beispiel als Hexadezimal-Code angeben. Das sieht dann so aus:

module.exports = {
  theme: {
    colors: {
      'white': '#ffffff',
      'purple': '#3f3cbb',
      'midnight': '#121063',
      'metal': '#565584',
    },
  },
}

Du hast allerdings auch die Möglichkeit Farben als CSS-Variablen anzulegen. Und genau das werden wir uns zunutze machen. Am gängigsten ist es Variablen im Pseudo-Element :root zu definieren. Grundsätzlich geht das aber in jedem CSS-Selektor. Wenn wir bei obigem Beispiel bleiben, dann würde das zum Beispiel so aussehen:

@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
  --color-white: #ffffff;
  --color-purple: #3f3cbb;
  --color-midnight: #121063;
  --color-metal: #565584;
}

Und in der Konfiguration könnten wir die Variablen so verwenden:

module.exports = {
  theme: {
    colors: {
      'white': 'var(--color-white)',
      'purple': 'var(--color-purple)',
      'midnight': 'var(--color-midnight)',
      'metal': 'var(--color-metal)',
    },
  },
}

An dieser Stelle ist wichtig zu wissen, dass man diese Variablen auch überschreiben kann. Das können wir nutzen, um auf diese Weise ein alternatives Farbschema zu ergänzen. In unserem Fall ist das natürlich ein Dark-Mode. Grundsätzlich könnte man das aber auch für die Umsetzung verschiedener Themes verwenden. Ein Hochkontrast-Modus für Menschen, die Probleme mit der visuellen Wahrnehmung haben, wäre beispielsweise ebenfalls denkbar.

Dark-Mode via CSS umsetzen

Einen Dark-Mode via CSS lässt sich über eine Media-Query realisieren. Die prefers-color-scheme-Eigenschaft kann mittlerweile in fast allen Browsern genutzt. Dadurch können wir auf den Benutzer-Wunsch reagieren und dann unsere Farbvariablen anpassen. In einem einfachen Beispiel könnte das dann so aussehen:

module.exports = {
  theme: {
    colors: {
      'light': 'var(--color-light)',
      'dark': 'var(--color-dark)',
    },
  },
}

Die Variablen definieren wir dann in CSS:

@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
  --color-light: #ffffff;
  --color-dark: #000000;
}

Via Media-Query überschreiben wir dann einfach die zuvor definierten Variablen:

@media (prefers-color-scheme: dark) {
  :root {
    --color-light: #000000;
    --color-dark: #ffffff;
  }
}

Und das ist es dann auch schon. Bei aktiviertem Dark-Mode werden die Farben ausgetauscht. Bei mehr Farben ist das natürlich ein wenig komplexer. Zudem wollt Ihr eventuell einige Farben auch beibehalten. Diese solltet ihr dann natürlich am besten nicht als Variablen definieren bzw. nicht via Media-Query verändern.

Dark-Mode via Javascript umsetzen

Aus verschiedenen Gründen kann es sinnvoll sein auf Javascript zu setzen, anstatt eine reine CSS-Lösung anzustreben. Zum Beispiel, wenn Du Benutzern die Möglichkeit geben willst, das Farbschema via Button umzuschalten. Grundsätzlich ist das vorgehen aber erst einmal recht ähnlich zu vorherigen Variante. Wir definieren die Farben als CSS-Variablen in der tailwind.config.js:

module.exports = {
  theme: {
    colors: {
      'light': 'var(--color-light)',
      'dark': 'var(--color-dark)',
    },
  },
}

Die Variablen definieren wir dann in CSS:

@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
  --color-light: #ffffff;
  --color-dark: #000000;
}

Anstatt hier nun mit einer Media-Query weiter zu machen, werden wir eine zusätzliche Klasse für den body einführen und diese dann zum Überschreiben der Variablen nutzen.

body.dark {
  --color-light: #000000;
  --color-dark: #ffffff;
}

Wir erstellen nun zusätzlich eine Javascript-Datei. In dieser nutzen wir matchMedia, um zu ermitteln, ob der Benutzer den Dark-Mode vorziehen würde. Das ist im Grunde genommen eine Media-Query auf Javascript-Ebene. Das Skript könnte dann so aussehen:

// Check if user prefers dark mode color scheme.
const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;

// If so, add the 'dark' class to the body element.
document.body.classList.toggle('dark', prefersDarkMode);

Damit sind wir erst einmal am Ziel. Allerdings bringt uns das natürlich zunächst einmal keine Vorteile gegenüber der CSS-Variante. Darum werden wir nun einen Umschalt-Button umsetzen. Dieser wird nicht nur die Benutzer-Präferenz respektieren, sondern auch das Umschalten des Farbschemas ermöglichen. Zudem werden wir uns die Auswahl des Benutzers merken.

Button zum Umschalten zwischen Light- & Dark-Mode

Zunächst einmal müssen wir einen Button in die Seite integrieren. Für dieses Tutorial ist das Aussehen egal. Wichtig ist nur, dass er existiert und für den Benutzer in irgendeiner Form zugänglich ist. Die einfachste Form wäre zum Beispiel so:

<button id="dark-mode-switch">
  Switch Color Scheme
</button>

Die id ist notwendig, damit wir den Button in Javascript ansprechen können. Die Umsetzung ist natürlich auch problemlos mit Vue, Alpine oder was auch immer möglich. Für dieses Beispiel werden ich ausschließlich Vanilla Javascript verwenden. Ein einfacher Umschalter ließe sich dann so umsetzen:

document
    .getElementById('dark-mode-switch')
    .addEventListener('click', () => document.body.classList.toggle('dark'));

Allerdings wollen wir natürlich auch die Benutzer-Präferenzen respektieren. Zusätzlich wollen wir die Auswahl des Benutzers speichern, sollte er sich anders entscheiden. Letzteres werden wir über Local Storage realisieren. Damit können wir dann eine Funktion erstellen, die ermittelt, ob wir den Dark-Mode nutzen sollen.

function useDarkMode() {
  // Check if we have stored the user preference using local storage.
  const colorScheme = window.localStorage.getItem('colorScheme');

  // If so, check if the color scheme should be dark. 
  if (colorScheme) {
    return colorScheme === 'dark';
  }
  
  // If not, use matchMedia to check the user's OS preferences.
  // This will return true, if the user wants to user dark mode.
  return window.matchMedia('(prefers-color-scheme: dark)').matches;
}

Das Ergebnis dieser Funktion können wir dann nutzen, um das gewünschte Farbschema einzustellen. Wir werden hier dann in jedem Fall immer gleich die Benutzer-Auswahl mit speichern.

function setDarkMode(enabled = false) {
  // Add or remove the 'dark' class.
  document.body.classList.toggle('dark', enabled);
  
  // Always store the user's preferences in local storage.
  window.localStorage.setItem('colorScheme', enabled ? 'dark' : 'light');
}

setDarkMode(useDarkMode());

Kombiniert können wir die beiden Funktionen mitsamt unserem Button nun nutzen, um das gewünschte Feature fertig zu stellen.

let darkModeEnabled = false;

document
    .getElementById('dark-mode-switch')
    .addEventListener('click', () => setDarkMode(!darkModeEnabled));

function useDarkMode() {
    const colorScheme = window.localStorage.getItem('colorScheme');
    if (colorScheme) {
        return colorScheme === 'dark';
    }

    return window.matchMedia('(prefers-color-scheme: dark)').matches;
}

function setDarkMode(enabled = false) {
    darkModeEnabled = enabled;
    document.body.classList.toggle('dark', enabled);
    window.localStorage.setItem('colorScheme', enabled ? 'dark' : 'light');
}

setDarkMode(useDarkMode());

Wie Du siehst gibt es verschiedene Möglichkeiten einen Dark-Mode mit Tailwind umzusetzen. Welche davon bevorzugst Du? Kennst Du vielleicht eine andere Herangehensweise, die interessant sein könnte? Dann schreib es gerne in den Kommentaren. Ich verabschiede mich an dieser Stelle. Gehab Dich wohl.

Noch keine Kommentare vorhanden.