Dark Mode CSS Implementierung
Dark Mode mit CSS implementieren
Ein guter Dark Mode verbessert die Benutzererfahrung und schont die Augen. Lernen Sie die Implementierung mit CSS Custom Properties und System-Präferenzen.
System-Präferenz erkennen
/* Automatisch: System-Einstellung nutzen */
@media (prefers-color-scheme: dark) {
body {
background: #1a1a1a;
color: #ffffff;
}
}
@media (prefers-color-scheme: light) {
body {
background: #ffffff;
color: #1a1a1a;
}
}
CSS Variables Strategie
/* Light Mode (Standard) */
:root {
--color-bg: #ffffff;
--color-bg-secondary: #f5f5f5;
--color-text: #1a1a1a;
--color-text-secondary: #666666;
--color-primary: #3b82f6;
--color-primary-hover: #2563eb;
--color-border: #e5e7eb;
--color-shadow: rgba(0, 0, 0, 0.1);
}
/* Dark Mode */
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #1a1a1a;
--color-bg-secondary: #2d2d2d;
--color-text: #ffffff;
--color-text-secondary: #a0a0a0;
--color-primary: #60a5fa;
--color-primary-hover: #93c5fd;
--color-border: #404040;
--color-shadow: rgba(0, 0, 0, 0.3);
}
}
/* Verwendung */
body {
background-color: var(--color-bg);
color: var(--color-text);
}
.card {
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
box-shadow: 0 4px 6px var(--color-shadow);
}
.button {
background: var(--color-primary);
}
.button:hover {
background: var(--color-primary-hover);
}
Manueller Toggle mit Data-Attribut
/* Light Mode (Standard) */
:root {
--color-bg: #ffffff;
--color-text: #1a1a1a;
/* ... weitere Variablen */
}
/* Dark Mode via Attribut */
[data-theme="dark"] {
--color-bg: #1a1a1a;
--color-text: #ffffff;
}
/* System-Präferenz als Fallback */
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--color-bg: #1a1a1a;
--color-text: #ffffff;
}
}
// JavaScript Toggle
function toggleTheme() {
const html = document.documentElement;
const currentTheme = html.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
html.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
}
// Beim Laden: Gespeicherte Präferenz anwenden
function initTheme() {
const savedTheme = localStorage.getItem('theme');
const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (savedTheme) {
document.documentElement.setAttribute('data-theme', savedTheme);
} else if (systemDark) {
document.documentElement.setAttribute('data-theme', 'dark');
}
}
// System-Änderungen live verfolgen
window.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', (e) => {
if (!localStorage.getItem('theme')) {
document.documentElement.setAttribute(
'data-theme',
e.matches ? 'dark' : 'light'
);
}
});
// Initialisieren
initTheme();
Komplettes Farbschema
:root {
/* Semantische Farben */
--color-success: #22c55e;
--color-warning: #f59e0b;
--color-error: #ef4444;
--color-info: #3b82f6;
/* Light Theme */
--color-bg-primary: #ffffff;
--color-bg-secondary: #f9fafb;
--color-bg-tertiary: #f3f4f6;
--color-text-primary: #111827;
--color-text-secondary: #6b7280;
--color-text-tertiary: #9ca3af;
--color-text-inverse: #ffffff;
--color-border-light: #e5e7eb;
--color-border-medium: #d1d5db;
--color-link: #2563eb;
--color-link-hover: #1d4ed8;
/* Elevations */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
}
[data-theme="dark"] {
/* Dark Theme Overrides */
--color-bg-primary: #111827;
--color-bg-secondary: #1f2937;
--color-bg-tertiary: #374151;
--color-text-primary: #f9fafb;
--color-text-secondary: #d1d5db;
--color-text-tertiary: #9ca3af;
--color-text-inverse: #111827;
--color-border-light: #374151;
--color-border-medium: #4b5563;
--color-link: #60a5fa;
--color-link-hover: #93c5fd;
/* Dunklere Schatten im Dark Mode */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.5);
/* Angepasste Status-Farben */
--color-success: #4ade80;
--color-warning: #fbbf24;
--color-error: #f87171;
--color-info: #60a5fa;
}
Toggle-Button Komponente
/* HTML */
<button class="theme-toggle" aria-label="Theme wechseln">
<span class="theme-toggle__icon theme-toggle__icon--light">☀️</span>
<span class="theme-toggle__icon theme-toggle__icon--dark">🌙</span>
</button>
/* CSS */
.theme-toggle {
position: relative;
width: 60px;
height: 30px;
border-radius: 15px;
border: none;
background: var(--color-bg-tertiary);
cursor: pointer;
transition: background 0.3s ease;
}
.theme-toggle__icon {
position: absolute;
top: 50%;
transform: translateY(-50%);
font-size: 16px;
transition: opacity 0.3s ease;
}
.theme-toggle__icon--light {
left: 8px;
opacity: 1;
}
.theme-toggle__icon--dark {
right: 8px;
opacity: 0.3;
}
[data-theme="dark"] .theme-toggle__icon--light {
opacity: 0.3;
}
[data-theme="dark"] .theme-toggle__icon--dark {
opacity: 1;
}
/* Slider-Stil */
.theme-toggle::after {
content: '';
position: absolute;
top: 3px;
left: 3px;
width: 24px;
height: 24px;
border-radius: 50%;
background: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
transition: transform 0.3s ease;
}
[data-theme="dark"] .theme-toggle::after {
transform: translateX(30px);
}
Bilder und Dark Mode
/* Bilder im Dark Mode dimmen */
[data-theme="dark"] img:not([src*=".svg"]) {
filter: brightness(0.9) contrast(1.05);
}
/* Unterschiedliche Bilder für Light/Dark */
<picture>
<source srcset="logo-dark.png" media="(prefers-color-scheme: dark)">
<img src="logo-light.png" alt="Logo">
</picture>
/* Oder mit CSS */
.logo {
background-image: url(/proxy.php?img=logo-light.png);
}
[data-theme="dark"] .logo {
background-image: url(/proxy.php?img=logo-dark.png);
}
/* Oder SVG mit currentColor */
.icon {
fill: currentColor;
}
Transitions beim Theme-Wechsel
/* Sanfte Übergänge */
:root {
--transition-theme: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}
body,
.card,
.button,
input,
textarea {
transition: var(--transition-theme);
}
/* Flash beim Laden verhindern */
html.no-transition,
html.no-transition * {
transition: none !important;
}
// Flash beim Laden verhindern
document.documentElement.classList.add('no-transition');
initTheme();
window.addEventListener('load', () => {
document.documentElement.classList.remove('no-transition');
});
FOUC (Flash of Unstyled Content) vermeiden
<!-- Im head, VOR dem CSS -->
<script>
(function() {
const theme = localStorage.getItem('theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
document.documentElement.setAttribute('data-theme', theme);
})();
</script>
color-scheme Property
/* Browser-UI anpassen (Scrollbars, Inputs, etc.) */
:root {
color-scheme: light dark;
}
/* Nur Light */
:root {
color-scheme: light;
}
/* Nur Dark */
[data-theme="dark"] {
color-scheme: dark;
}
/* Meta-Tag für Browser-Chrome */
<meta name="color-scheme" content="light dark">
💡 Best Practices:
1. Nutzen Sie CSS Custom Properties für alle Farben
2. Respektieren Sie die System-Präferenz als Standard
3. Speichern Sie manuelle Auswahl in localStorage
4. Verwenden Sie
5. Testen Sie Kontraste für Barrierefreiheit (WCAG)
2. Respektieren Sie die System-Präferenz als Standard
3. Speichern Sie manuelle Auswahl in localStorage
4. Verwenden Sie
color-scheme für native UI-Elemente5. Testen Sie Kontraste für Barrierefreiheit (WCAG)