Toggle Night Mode

Der Theme-Toggle ermöglicht das Wechseln zwischen Dark Mode und Light Mode.

Toggle Night Mode

Der Theme-Toggle ermöglicht das Wechseln zwischen Dark Mode und Light Mode. Das gewählte Theme wird im localStorage gespeichert und über mehrere Tabs synchronisiert.

Button-Platzierung im Header

Der Theme-Toggle wird im Header zwischen Logo und Mobile-Menu-Button platziert:

HTML
<header class="site-header">
    <div class="container">
        <div class="header-content">
            <!-- Logo -->
            <div class="brand">...</div>

            <!-- Theme Toggle -->
            <button class="theme-toggle" id="themeToggle" aria-label="Dark Mode umschalten">
                <span class="theme-icon">
                    <img src="/assets/icons/dark-mode.svg" alt="Copy Icon" width="20" height="20">
                </span>
            </button>

            <!-- Mobile Menu Button -->
            <button class="mobile-menu-toggle" id="mobileMenuToggle">...</button>

            <!-- Navigation -->
            <nav class="main-nav">...</nav>
        </div>
    </div>
</header>

Wie funktioniert das Theme-System?

Das Theme-System basiert auf dem data-theme Attribut am <html> Element:

  • Dark Mode: <html data-theme="dark">
  • Light Mode: <html data-theme="light">

CSS-Variablen ändern sich automatisch basierend auf dem Theme:

CSS
/* Dark Mode (Standard) */
:root {
    --bg-primary: #1a1d29;
    --text-primary: #e4e6eb;
    --accent-blue: #4a9eff;
}

/* Light Mode */
[data-theme="light"] {
    --bg-primary: #ffffff;
    --text-primary: #1a1d29;
    --accent-blue: #0066cc;
}

Icon-Logik: Warum wechselt das Icon?

Das Icon zeigt immer das andere Theme an (nicht das aktuelle!):

  • Im Dark Mode: Zeige Light-Icon → "Klick mich für Light Mode"
  • Im Light Mode: Zeige Dark-Icon → "Klick mich für Dark Mode"

Das ist intuitiver als ein Icon, das den aktuellen Zustand zeigt.

Wie funktioniert das technisch?

Die Magie passiert in der updateThemeIcon()-Funktion. Der entscheidende Trick ist diese Zeile:

JavaScript
// Das "umgekehrte" Icon wählen:
const iconSrc = theme === 'dark' ? THEME_ICONS.light : THEME_ICONS.dark;

Der ternäre Operator ? : prüft: "Ist das aktuelle Theme dark? Dann nimm das light-Icon, sonst das dark-Icon."

Die vollständige Funktion und wie sie mit dem Click-Event zusammenspielt, findest du weiter unten im vollständigen JavaScript-Code.

Icon-Pfade definieren

Die Icon-Pfade werden am Anfang des JavaScripts definiert:

JavaScript
const THEME_ICONS = {
    dark: '/assets/icons/light-mode.svg',   // Im Dark Mode: zeige Sonne → "Wechsel zu Light"
    light: '/assets/icons/dark-mode.svg'    // Im Light Mode: zeige Mond → "Wechsel zu Dark"  
};

Eigene Icons verwenden

Icon-Anforderungen

Wichtig bei der Auswahl eigener Icons:

  • Form: Icons sollten quadratisch oder rund sein (1:1 Verhältnis)
  • Größe: 20x20px oder 24x24px (viewBox anpassen)
  • Format: SVG bevorzugt (für scharfe Darstellung)
  • Style: Einfache, klare Symbole (Mond/Sonne, Toggle, etc.)

Probleme mit anderen Icon-Formen:

  • Slider/Switch-Icons (rechteckig) passen nicht in den runden Button
  • Der Button hat eine border-radius, die bei nicht-quadratischen Icons stört
  • Rechteckige Icons werden gequetscht oder verzerrt

Um eigene Icons zu verwenden:

  1. Platziere deine Icons in /assets/icons/
  2. Benenne sie z.B. mond.svg und sonne.svg
  3. Aktualisiere die Pfade im JavaScript:
JavaScript
const THEME_ICONS = {
    dark: '/assets/icons/sonne.svg',   // Im Dark Mode: zeige Sonne
    light: '/assets/icons/mond.svg'    // Im Light Mode: zeige Mond
};

Theme-Speicherung & Persistenz

Das gewählte Theme wird automatisch im localStorage gespeichert:

  • Beim Laden: Das zuletzt gewählte Theme wird wiederhergestellt
  • Standard: Wenn kein Theme gespeichert ist, wird Dark Mode verwendet
  • Tab-Synchronisation: Theme-Wechsel in einem Tab werden in allen anderen Tabs übernommen
Vollständiger JavaScript-Code
JavaScript
// ===================================
// THEME TOGGLE (Dark/Light Mode) mit Icon-Wechsel
// ===================================

const themeToggle = document.getElementById('themeToggle');
const currentTheme = localStorage.getItem('theme') || 'dark';

// Setze initial Theme
document.documentElement.setAttribute('data-theme', currentTheme);

// Funktion: Icon aktualisieren basierend auf aktuellem Theme
function updateThemeIcon(theme) {
    if (!themeToggle) return;

    // Finde das img Element innerhalb des Buttons
    const img = themeToggle.querySelector('.theme-icon img');
    if (!img) return;

    // Dark Mode = zeige Light Icon (zum Wechseln zu Light)
    // Light Mode = zeige Dark Icon (zum Wechseln zu Dark)
    const iconSrc = theme === 'dark' ? THEME_ICONS.light : THEME_ICONS.dark;
    img.src = iconSrc;

    // Accessibility: Aria-Label aktualisieren
    const label = theme === 'dark' ? 'Zu Light Mode wechseln' : 'Zu Dark Mode wechseln';
    themeToggle.setAttribute('aria-label', label);
}

// Initial Icon setzen
updateThemeIcon(currentTheme);

if (themeToggle) {
    themeToggle.addEventListener('click', () => {
        const current = document.documentElement.getAttribute('data-theme');
        const newTheme = current === 'dark' ? 'light' : 'dark';

        document.documentElement.setAttribute('data-theme', newTheme);
        localStorage.setItem('theme', newTheme);

        // Icon aktualisieren
        updateThemeIcon(newTheme);

        console.log(`🌓 Theme gewechselt: ${current} → ${newTheme}`);
    });
}

// Theme synchronisieren bei Tab-Wechsel
window.addEventListener('storage', (e) => {
    if (e.key === 'theme') {
        const newTheme = e.newValue || 'dark';
        document.documentElement.setAttribute('data-theme', newTheme);
        updateThemeIcon(newTheme);
    }
});

Standard-Theme ändern

Um Light Mode als Standard zu verwenden (statt Dark Mode):

JavaScript
// Ändere 'dark' zu 'light'
const currentTheme = localStorage.getItem('theme') || 'light';

Button-Styling anpassen

Das Styling des Theme-Toggle-Buttons findest du in style.css unter "Header & Navigation":

CSS
.theme-toggle {
    background: transparent;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 0.5rem;
    cursor: pointer;
    transition: all 200ms ease;
}

.theme-toggle:hover {
    background-color: var(--bg-secondary);
    border-color: var(--accent-blue);
}

.theme-icon img {
    display: block;
    width: 20px;
    height: 20px;
}

Icon-Probleme lösen

Wenn du Slider/Toggle-Icons (rechteckige Form, z.B. 40x20px) verwendest, wird oft nur eine Ecke angezeigt. Das liegt am CSS:

CSS
/* Aktuelles CSS (für quadratische Icons) */
.theme-icon img {
    display: block;
    width: 20px;      /* ← Erzwingt 20px Breite */
    height: 20px;     /* ← Erzwingt 20px Höhe */
}

Was passiert:

  • Ein 40x20px Icon wird auf 20x20px gequetscht
  • Der Browser zeigt nur den linken Teil (erste 20px Breite)
  • Der Rest wird abgeschnitten → Nur eine Ecke sichtbar

Lösung 1: CSS für rechteckige Icons anpassen

Um Slider-Icons korrekt anzuzeigen, muss das CSS geändert werden:

CSS
/* Für Slider-Icons (rechteckig, z.B. 40x20px) */
.theme-icon img {
    display: block;
    width: auto;           /* ← Breite automatisch */
    height: 20px;          /* ← Nur Höhe fixieren */
    max-width: 50px;       /* ← Maximale Breite begrenzen */
    object-fit: contain;   /* ← Seitenverhältnis beibehalten */
}

Oder für mehr Kontrolle:

CSS
/* Für Slider-Icons - spezifische Größe */
.theme-icon img {
    display: block;
    width: 40px;           /* ← Breite des Sliders */
    height: 20px;          /* ← Höhe des Sliders */
    object-fit: contain;   /* ← Wichtig! */
}

Lösung 2: Button-Container anpassen

Der Button selbst muss auch breiter werden, um den Slider aufzunehmen:

CSS
.theme-toggle {
    background: transparent;
    border: 1px solid var(--border-color);
    border-radius: 12px;      /* ← Mehr Radius für rechteckige Form */
    padding: 0.4rem 0.6rem;   /* ← Mehr horizontales Padding */
    cursor: pointer;
    transition: all 200ms ease;
    /* Optional: */
    min-width: 50px;          /* ← Mindestbreite für Slider */
}
Warum Slider-Icons trotzdem problematisch sind

Auch mit angepasstem CSS gibt es Probleme:

  • Design-Inkonsistenz: Der Button wird rechteckig und passt nicht zu den anderen runden Buttons im Header
  • Border-Radius stört: Die abgerundeten Ecken passen nicht zur rechteckigen Icon-Form
  • Responsive Probleme: Auf mobilen Geräten nimmt der breitere Button mehr Platz weg
  • Visuelle Hierarchie: Ein breiterer Button zieht mehr Aufmerksamkeit als gewünscht

Empfohlene Alternative zu Slider-Icons

Statt Slider-Icons funktionieren diese Varianten besser:

Gut: Mond/Sonne Icons

  • Quadratisch (z.B. 20x20px)
  • Klare Symbole
  • Universell verständlich

Gut: Toggle-Kreis Icons

  • Rund (Durchmesser 20px)
  • Ein/Aus Symbol
  • Minimalistisch

Problematisch: Slider/Switch

  • Rechteckig (z.B. 40x20px)
  • Benötigt mehr Platz
  • Passt nicht in runden Button

Problematisch: Text-Icons

  • Zu groß/breit
  • Nicht responsive-freundlich
  • Lokalisierungsprobleme

Bonus: SVG viewBox richtig einstellen

Wenn dein Icon trotz korrektem CSS abgeschnitten wird, prüfe den viewBox im SVG:

SVG
<!-- Falscher viewBox (zeigt nur Ecke) -->
<svg viewBox="0 0 20 20" width="40" height="20">
    <!-- Icon ist 40x20, aber viewBox ist 20x20 → abgeschnitten! -->
</svg>

<!-- Richtiger viewBox (zeigt alles) -->
<svg viewBox="0 0 40 20" width="40" height="20">
    <!-- viewBox passt zu tatsächlicher Icon-Größe ✓ -->
</svg>

Regel: Der viewBox muss zur tatsächlichen Zeichenfläche des Icons passen!

Mehr aus DevPanicZone!

Tutorials werden geladen...