CSS Custom Properties für dynamische Farbsysteme
Embed This Widget
Add the script tag and a data attribute to embed this widget.
Embed via iframe for maximum compatibility.
<iframe src="https://colorfyi.com/iframe/entity//" width="420" height="400" frameborder="0" style="border:0;border-radius:10px;max-width:100%" loading="lazy"></iframe>
Paste this URL in WordPress, Medium, or any oEmbed-compatible platform.
https://colorfyi.com/entity//
Add a dynamic SVG badge to your README or docs.
[](https://colorfyi.com/entity//)
Use the native HTML custom element.
CSS Custom Properties – gemeinhin als CSS-Variablen bezeichnet – sind die Grundlage jedes modernen, wartbaren Farbsystems. Sie ermöglichen es, eine Farbe einmalig zu definieren und überall zu referenzieren, wodurch globale Designänderungen zu einer einzeiligen Bearbeitung werden statt zu einem Suchen-und-Ersetzen in Hunderten von Dateien. In Kombination mit der Kaskade und JavaScript erschließen sie dynamische Theming-Fähigkeiten, die Präprozessoren wie Sass oder Less schlichtweg nicht bieten können.
Dieses Tutorial führt durch den Aufbau eines produktionsreifen Farbsystems von Grund auf mit CSS Custom Properties: von den Grundlagen über Hell-/Dunkel-Modus-Umschaltung und Überschreibungen auf Komponentenebene bis hin zur Laufzeit-Farbmanipulation mit JavaScript.
CSS-Variablen-Grundlagen
Eine CSS Custom Property ist jede Eigenschaft, deren Name mit zwei Bindestrichen (--) beginnt. Sie deklarieren sie wie jede andere Eigenschaft und lesen sie mit der Funktion var() aus.
:root {
--color-brand: #2563EB;
}
.button {
background-color: var(--color-brand);
}
Der :root-Selektor entspricht dem html-Element, hat aber eine höhere Spezifität und ist der konventionelle Ort für globale Tokens. Jedes Element im Dokument kann --color-brand lesen.
Fallback-Werte
Die Funktion var() akzeptiert einen optionalen Fallback, der verwendet wird, wenn die Variable undefiniert oder ungültig ist:
.card {
/* Fällt auf #6B7280 zurück, wenn --color-secondary nicht definiert ist */
color: var(--color-secondary, #6B7280);
}
Fallbacks können verschachtelt werden – das zweite Argument von var() darf selbst var() verwenden –, was für mehrschichtige Design-Token-Systeme nützlich ist.
Custom Properties vs. Präprozessor-Variablen
Sass- und Less-Variablen werden zur Kompilierzeit aufgelöst und in das generierte CSS eingebacken. Einmal kompiliert, sind sie verschwunden. CSS Custom Properties leben im Browser und reagieren auf die Kaskade, Vererbung und JavaScript. Diese Unterscheidung ermöglicht alles, was in diesem Tutorial folgt.
Farbthema-Architektur
Ein gut gestaltetes Token-System hat mindestens zwei Schichten: primitive Tokens (Rohdaten) und semantische Tokens (zweckgerichtete Referenzen).
Primitive Tokens: Die vollständige Palette
Primitive Tokens definieren jede Farbe, die Ihr System verwenden kann. Die beste Praxis besteht darin, eine vollständige Schattenskala für jeden benötigten Farbton zu generieren. Verwenden Sie den Schattengenerator, um eine Tailwind-ähnliche 50–950-Skala für jede Grundfarbe zu erstellen.
Beginnend beispielsweise mit einem Markenblau #2563EB:
:root {
/* Blau-Skala — generiert aus #2563EB */
--blue-50: #EFF6FF;
--blue-100: #DBEAFE;
--blue-200: #BFDBFE;
--blue-300: #93C5FD;
--blue-400: #60A5FA;
--blue-500: #3B82F6;
--blue-600: #2563EB;
--blue-700: #1D4ED8;
--blue-800: #1E40AF;
--blue-900: #1E3A8A;
--blue-950: #172554;
/* Neutrale Skala */
--gray-50: #F9FAFB;
--gray-100: #F3F4F6;
--gray-200: #E5E7EB;
--gray-300: #D1D5DB;
--gray-400: #9CA3AF;
--gray-500: #6B7280;
--gray-600: #4B5563;
--gray-700: #374151;
--gray-800: #1F2937;
--gray-900: #111827;
--gray-950: #030712;
}
Primitive Tokens werden nie direkt in Komponentenstilen verwendet. Sie existieren ausschließlich, um von semantischen Tokens referenziert zu werden.
Semantische Tokens: Absicht statt Wert
Semantische Tokens verleihen den Rohdaten Bedeutung. Statt --blue-600 verwendet Ihre Komponente --color-action-primary. Diese Abstraktion ermöglicht es Ihnen, --color-action-primary später einem Grün oder Lila neu zuzuweisen, woraufhin jede Komponente, die es verwendet, automatisch aktualisiert wird.
:root {
/* Hintergrund */
--color-bg-base: var(--gray-50);
--color-bg-surface: #FFFFFF;
--color-bg-elevated: #FFFFFF;
--color-bg-subtle: var(--gray-100);
/* Text */
--color-text-primary: var(--gray-900);
--color-text-secondary: var(--gray-600);
--color-text-disabled: var(--gray-400);
--color-text-inverse: #FFFFFF;
/* Marke / Aktion */
--color-action-primary: var(--blue-600);
--color-action-primary-hover: var(--blue-700);
--color-action-primary-text: #FFFFFF;
/* Rahmen */
--color-border-base: var(--gray-200);
--color-border-strong: var(--gray-400);
/* Feedback */
--color-success: #16A34A;
--color-warning: #D97706;
--color-error: #DC2626;
--color-info: var(--blue-600);
}
Komponentenstile referenzieren dann ausschließlich semantische Tokens:
.button-primary {
background-color: var(--color-action-primary);
color: var(--color-action-primary-text);
border: 1px solid var(--color-action-primary);
}
.button-primary:hover {
background-color: var(--color-action-primary-hover);
}
.card {
background-color: var(--color-bg-surface);
border: 1px solid var(--color-border-base);
color: var(--color-text-primary);
}
Diese zweischichtige Architektur hält das System flexibel und refaktorierbar. Wenn sich Ihre Markenfarbe ändert, aktualisieren Sie ein primitives Token und die semantische Schicht überträgt die Änderung überall.
Hell-/Dunkel-Umschaltmuster
CSS Custom Properties werden durch den Dokumentenbaum vererbt, was bedeutet, dass Sie ein Token in einem bestimmten Geltungsbereich neu definieren können und jeder Nachkomme den neuen Wert erbt. Das ist der Mechanismus, der Hell-/Dunkel-Theming trivial macht.
Ansatz 1: prefers-color-scheme (Automatisch)
Die einfachste Implementierung reagiert auf die Betriebssystemeinstellung des Benutzers:
:root {
/* Hell-Modus (Standard) */
--color-bg-base: #FFFFFF;
--color-bg-surface: #F9FAFB;
--color-text-primary: #111827;
--color-text-secondary: #6B7280;
--color-border-base: #E5E7EB;
--color-action-primary: #2563EB;
}
@media (prefers-color-scheme: dark) {
:root {
/* Dunkel-Modus-Überschreibungen */
--color-bg-base: #111827;
--color-bg-surface: #1F2937;
--color-text-primary: #F9FAFB;
--color-text-secondary: #9CA3AF;
--color-border-base: #374151;
--color-action-primary: #3B82F6;
}
}
Jede Komponente erhält automatisch die richtigen Farben. Kein JavaScript erforderlich. Das Komponenten-CSS ändert sich überhaupt nicht.
Ansatz 2: Data-Attribut-Toggle (Manuell)
Für benutzergesteuerte Themenumschaltung ist das übliche Muster ein data-theme-Attribut auf dem html- oder body-Element:
:root,
[data-theme="light"] {
--color-bg-base: #FFFFFF;
--color-text-primary: #111827;
--color-action-primary: #2563EB;
}
[data-theme="dark"] {
--color-bg-base: #111827;
--color-text-primary: #F9FAFB;
--color-action-primary: #60A5FA;
}
Beachten Sie, dass der Dunkel-Modus #60A5FA (blau-400) statt #2563EB (blau-600) für die primäre Aktionsfarbe verwendet. Auf dunklen Hintergründen behalten hellere Blautöne den barrierefreien Kontrast bei. Verwenden Sie den Kontrast-Prüfer, um zu überprüfen, dass jedes semantische Token-Paar (Text auf Hintergrund) WCAG AA in beiden Modi besteht.
Schalten Sie das Theme mit einer kleinen JavaScript-Funktion um:
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('color-theme', theme);
}
// Beim Seitenaufruf gespeicherte Präferenz berücksichtigen
const saved = localStorage.getItem('color-theme');
if (saved) {
setTheme(saved);
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
setTheme('dark');
}
// Umschalttaste
document.getElementById('theme-toggle').addEventListener('click', () => {
const current = document.documentElement.getAttribute('data-theme');
setTheme(current === 'dark' ? 'light' : 'dark');
});
Vermeidung von Flash of Unstyled Content
Bei der Verwendung von localStorage für Theme-Persistenz platzieren Sie ein blockierendes Inline-Skript im <head> – vor allen Stylesheets –, um das gespeicherte Theme-Attribut anzuwenden, bevor der Browser zeichnet:
<head>
<script>
const theme = localStorage.getItem('color-theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
document.documentElement.setAttribute('data-theme', theme);
</script>
<link rel="stylesheet" href="styles.css">
</head>
Dieses kleine Inline-Skript stellt sicher, dass das korrekte Theme angewendet wird, bevor CSS geparst wird, und eliminiert den Weiß-Flash-dann-Dunkel-Übergang, der schlecht implementierte Dunkel-Modi plagt.
Farbüberschreibungen auf Komponentenebene
Eine der mächtigsten Funktionen von CSS Custom Properties sind bereichsspezifische Überschreibungen – ein Token innerhalb des Geltungsbereichs einer bestimmten Komponente neu zu definieren, sodass deren Kinder den lokalen Wert erben.
Anwendungsfall: Invertierter (dunkler) Hero-Abschnitt
Angenommen, Sie haben ein globales semantisches Token --color-bg-base, das auf Weiß gesetzt ist, und möchten einen dunklen Hero-Abschnitt, in dem sich der Text automatisch anpasst:
.hero--dark {
--color-bg-base: #111827;
--color-text-primary: #F9FAFB;
--color-text-secondary: #9CA3AF;
--color-border-base: #374151;
background-color: var(--color-bg-base);
color: var(--color-text-primary);
}
/* Keine Änderungen nötig — .hero__title erbt die überschriebenen Werte */
.hero__title {
color: var(--color-text-primary);
}
.hero__subtitle {
color: var(--color-text-secondary);
}
Der .hero--dark-Modifikator definiert die Tokens lokal neu. Jedes Element innerhalb von .hero--dark, das diese Tokens verwendet, erbt automatisch die dunklen Werte, ohne jegliche Klassen-Duplizierung wie hero__title--dark.
Anwendungsfall: Markierte Alert-Varianten
Überschreibungen auf Komponentenebene sind ideal für semantische Varianten:
.alert {
background-color: var(--alert-bg, var(--color-bg-subtle));
color: var(--alert-text, var(--color-text-primary));
border-left: 4px solid var(--alert-accent, var(--color-border-strong));
padding: 1rem 1.25rem;
border-radius: 0.375rem;
}
.alert--success {
--alert-bg: #F0FDF4;
--alert-text: #14532D;
--alert-accent: #16A34A;
}
.alert--error {
--alert-bg: #FEF2F2;
--alert-text: #7F1D1D;
--alert-accent: #DC2626;
}
.alert--warning {
--alert-bg: #FFFBEB;
--alert-text: #78350F;
--alert-accent: #D97706;
}
Die .alert-Basiskomponente referenziert private Variablen auf Komponentenebene (--alert-bg, --alert-text, --alert-accent) mit Fallbacks zu globalen Tokens. Die Varianten-Modifikatoren setzen diese privaten Variablen. Dieses Muster hält das CSS der Basiskomponente sauber und macht das Hinzufügen neuer Varianten trivial.
Laufzeit-Farbänderungen mit JavaScript
Da CSS Custom Properties live im Browser sind, kann JavaScript sie jederzeit lesen und schreiben – und ermöglicht so dynamische Farbsysteme, die auf Benutzereingaben, Daten oder Anwendungszustand reagieren.
Custom Properties lesen und schreiben
const root = document.documentElement;
// Eine Custom Property lesen
const brandColor = getComputedStyle(root).getPropertyValue('--color-action-primary').trim();
console.log(brandColor); // '#2563EB'
// Eine Custom Property setzen
root.style.setProperty('--color-action-primary', '#7C3AED');
// Eine Überschreibung entfernen (kehrt zum Stylesheet-Wert zurück)
root.style.removeProperty('--color-action-primary');
Anwendungsfall: Benutzerdefinierbare Markenfarbe
Angenommen, Sie bauen ein SaaS-Dashboard, bei dem jeder Mandant seine Markenfarbe setzen kann. Sie erhalten die Markenfarbe von einer API und wenden sie zur Laufzeit an:
async function applyTenantTheme(brandHex) {
// brandHex: '#7C3AED' (ein Lila)
const root = document.documentElement;
// Primäre Aktionsfarbe setzen
root.style.setProperty('--color-action-primary', brandHex);
// Eine Hover-Variante ableiten (serverseitig oder über eine kleine Bibliothek berechnet)
const hoverHex = darkenColor(brandHex, 0.1);
root.style.setProperty('--color-action-primary-hover', hoverHex);
}
Der Farbkonverter kann Ihnen dabei helfen, herauszufinden, welchen OKLCH- oder HSL-Werten ein HEX-Code entspricht, was nützlich ist, wenn Sie Schattierungsableitungslogik aufbauen. Zum Beispiel ist #7C3AED in OKLCH ungefähr oklch(0.52 0.23 295) – Sie können hellere und dunklere Varianten ableiten, indem Sie die L-Komponente anpassen, während Sie C und H konstant halten.
Anwendungsfall: Echtzeit-Farbthema-Auswahl
<input type="color" id="brand-picker" value="#2563EB">
document.getElementById('brand-picker').addEventListener('input', (e) => {
document.documentElement.style.setProperty('--color-action-primary', e.target.value);
});
Das ist ein echter Live-Theme-Editor in zwölf Codezeilen. Kein Build-Schritt, keine Bibliothek, kein Re-Render-Zyklus. Der Browser aktualisiert jedes Element, das --color-action-primary verwendet, sofort, während der Benutzer den Farbwähler bedient.
Custom Properties animieren
Custom Properties können auch mit @property interpoliert werden, der Houdini-API, mit der Sie den Typ und den Anfangswert einer Eigenschaft deklarieren können:
@property --gradient-angle {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
}
.animated-border {
--gradient-angle: 0deg;
background: conic-gradient(
from var(--gradient-angle),
#2563EB, #7C3AED, #EC4899, #2563EB
);
animation: rotate 4s linear infinite;
}
@keyframes rotate {
to { --gradient-angle: 360deg; }
}
Ohne @property weiß der Browser nicht, dass --gradient-angle ein Winkel ist, und kann ihn nicht interpolieren. Mit dieser Deklaration läuft die Animation flüssig.
Wesentliche Erkenntnisse
- CSS Custom Properties sind Live-Werte, keine Kompilierzeit-Ersetzungen. Die Kaskade, Vererbung und JavaScript gelten alle – und geben Ihnen Fähigkeiten, die kein Präprozessor-Variablensystem bieten kann.
- Verwenden Sie eine zweischichtige Token-Architektur: primitive Tokens (Rohfarbwerte) und semantische Tokens (zweckgerichtete Referenzen). Komponenten referenzieren ausschließlich semantische Tokens. Das macht umfangreiches Refactoring zu einer einzeiligen Änderung.
- Hell-/Dunkel-Modus ist nur eine Token-Neudefinition: Verwenden Sie
@media (prefers-color-scheme: dark)für automatische Umschaltung oder eindata-theme-Attribut für manuelle Steuerung. Komponenten benötigen nie variantenspezifisches CSS. - Überschreibungen auf Komponentenebene ermöglichen invertierte Abschnitte und semantische Varianten durch lokales Neudefinieren von Tokens – ohne zusätzliche Klassennamen oder doppeltes CSS.
- JavaScript kann Custom Properties zur Laufzeit lesen und schreiben, wodurch Live-Theme-Editoren, mandantenspezifisches Branding und datengesteuerte Farbänderungen ermöglicht werden.
- Überprüfen Sie immer den barrierefreien Kontrast sowohl im Hell- als auch im Dunkel-Modus mit dem Kontrast-Prüfer. Dunkelmodus-Blautöne und Grüntöne müssen oft heller als ihre Hellmodus-Gegenstücke sein, um ausreichende Kontrastverhältnisse zu gewährleisten.
- Verwenden Sie den Schattengenerator, um vollständige, gleichmäßig abgestufte Farbskalen für Ihre primitive Token-Palette zu erstellen, und den Farbkonverter, um zwischen HEX, RGB, HSL und OKLCH zu übersetzen, wenn Sie Ableitungslogik aufbauen.