RGB und Hex: Die Grundlage der Bildschirmfarben
Jeder Pixel auf deinem Bildschirm mischt Licht aus drei Kanälen: Rot, Grün und Blau (RGB). Jeder Kanal hat einen Wert von 0 bis 255 (8 Bit). Zusammen ergeben sich 256³ = 16.777.216 mögliche Farben. rgb(255, 0, 0) ist reines Rot, rgb(0, 0, 0) ist Schwarz, rgb(255, 255, 255) ist Weiß.
Hex-Codes sind nichts anderes als RGB-Werte in hexadezimaler Notation: #FF0000 = rgb(255, 0, 0). Die ersten zwei Stellen sind Rot, die mittleren Grün, die letzten Blau. Die Kurzform #F00 verdoppelt jede Stelle automatisch zu #FF0000. Mit Alpha-Kanal: #FF000080 hat 50 % Transparenz.
Das Problem mit RGB: Es ist nicht intuitiv. Welche RGB-Werte ergeben ein helles Blaugrün? Wie machst du eine Farbe 20 % heller ohne den Farbton zu verschieben? RGB mischt Lichtanteile — das entspricht nicht der menschlichen Farbwahrnehmung. Deshalb gibt es alternative Farbmodelle.
Trotzdem bleibt RGB der technische Standard: Browser rendern in RGB, Grafikarten arbeiten in RGB, und Bilddateien speichern RGB-Werte. Alle anderen Farbmodelle werden intern in RGB konvertiert. Hex-Codes sind in CSS weiterhin die kompakteste Notation für feste Farben.
/* RGB und Hex — verschiedene Schreibweisen für dasselbe */
.element {
color: #3498db; /* Hex (6 Stellen) */
color: #3498dbcc; /* Hex mit Alpha (8 Stellen, 80% opak) */
color: rgb(52, 152, 219); /* RGB funktional */
color: rgb(52 152 219); /* RGB modern (ohne Kommas) */
color: rgb(52 152 219 / 80%); /* RGB mit Alpha (moderne Syntax) */
}
/* Hex-Kurzform: jede Stelle wird verdoppelt */
#F00 → #FF0000 /* Rot */
#0F0 → #00FF00 /* Grün */
#09C → #0099CC /* Blaugrau */
#F008 → #FF000088 /* Rot mit ~53% Alpha */HSL: Farben wie ein Mensch denkt
HSL steht für Hue (Farbton, 0–360°), Saturation (Sättigung, 0–100%) und Lightness (Helligkeit, 0–100%). Der Farbton ist ein Winkel auf dem Farbkreis: 0° = Rot, 120° = Grün, 240° = Blau. 50 % Helligkeit ist die reine Farbe, 0 % ist Schwarz, 100 % ist Weiß.
Der große Vorteil: Farbmanipulation ist intuitiv. Eine Farbe 20 % heller machen? Erhöhe L um 20. Den Farbton um 30° drehen? Addiere 30 zum H-Wert. Eine gedämpfte Version erstellen? Reduziere S. Das ist mit RGB praktisch nicht möglich ohne komplexe Berechnungen.
HSL eignet sich hervorragend für Farbsysteme: Definiere eine Basisfarbe (z.B. hsl(210, 80%, 50%) als Primärfarbe) und leite Varianten ab durch Änderung von S und L. Hover-Zustand: L + 10%. Disabled: S - 40%. Dark-Mode-Variante: L invertieren. Ein System, das auf jedem Farbton funktioniert.
Der Nachteil von HSL: Es ist nicht perzeptuell gleichmäßig. hsl(60, 100%, 50%) (Gelb) wirkt optisch viel heller als hsl(240, 100%, 50%) (Blau), obwohl beide L=50% haben. Für barrierefreie Kontraste und gleichmäßige Farbskalen ist HSL daher nicht ideal — dafür gibt es OKLCH.
/* HSL für ein Farbsystem mit Varianten */
:root {
--primary-h: 210;
--primary-s: 80%;
--primary-l: 50%;
--primary: hsl(var(--primary-h), var(--primary-s), var(--primary-l));
--primary-light: hsl(var(--primary-h), var(--primary-s), 65%);
--primary-dark: hsl(var(--primary-h), var(--primary-s), 35%);
--primary-muted: hsl(var(--primary-h), 30%, var(--primary-l));
}
/* Hover/Active-Zustände über Lightness steuern */
.button {
background: hsl(210, 80%, 50%);
}
.button:hover {
background: hsl(210, 80%, 60%); /* Heller */
}
.button:active {
background: hsl(210, 80%, 40%); /* Dunkler */
}OKLCH: Die Zukunft der CSS-Farben
OKLCH (Oklab Lightness, Chroma, Hue) ist ein perzeptuell gleichmäßiger Farbraum — gleiche numerische Abstände entsprechen gleichen wahrgenommenen Unterschieden. Das löst das größte HSL-Problem: In OKLCH hat oklch(0.7, 0.15, 60) (Gelb) und oklch(0.7, 0.15, 260) (Blau) tatsächlich die gleiche wahrgenommene Helligkeit.
Die drei Komponenten: L (Lightness, 0–1) ist die wahrgenommene Helligkeit, C (Chroma, 0–~0.4) ist die Farbintensität (ähnlich Sättigung), und H (Hue, 0–360°) ist der Farbton wie bei HSL. Der Wertebereich von Chroma hängt vom Farbton ab — manche Farbtöne können intensiver sein als andere.
Für Entwickler bedeutet das: Farbpaletten mit gleichmäßigem visuellem Abstand sind trivial zu erstellen. Nimm eine Basisfarbe, variiere nur L in gleichmäßigen Schritten, und du bekommst eine Palette die tatsächlich gleichmäßig aussieht. Mit HSL funktioniert das nicht, weil L nicht der menschlichen Wahrnehmung entspricht.
Browser-Support 2026: OKLCH wird von allen modernen Browsern unterstützt (Chrome 111+, Firefox 113+, Safari 15.4+). Für ältere Browser kannst du @supports oder den Fallback-Syntax verwenden. PostCSS-Plugins konvertieren OKLCH automatisch in RGB für Legacy-Browser.
/* OKLCH für gleichmäßige Farbskalen */
:root {
/* Blaue Palette mit gleichmäßiger wahrgenommener Helligkeit */
--blue-100: oklch(0.95, 0.03, 240);
--blue-200: oklch(0.85, 0.06, 240);
--blue-300: oklch(0.75, 0.10, 240);
--blue-400: oklch(0.65, 0.14, 240);
--blue-500: oklch(0.55, 0.18, 240); /* Basisfarbe */
--blue-600: oklch(0.45, 0.16, 240);
--blue-700: oklch(0.35, 0.13, 240);
--blue-800: oklch(0.25, 0.09, 240);
--blue-900: oklch(0.15, 0.05, 240);
}
/* Farbton-Rotation in OKLCH — gleichmäßig wahrgenommen */
.chip-1 { background: oklch(0.7, 0.15, 0); } /* Rot */
.chip-2 { background: oklch(0.7, 0.15, 60); } /* Orange */
.chip-3 { background: oklch(0.7, 0.15, 120); } /* Grün */
.chip-4 { background: oklch(0.7, 0.15, 180); } /* Cyan */
.chip-5 { background: oklch(0.7, 0.15, 240); } /* Blau */
.chip-6 { background: oklch(0.7, 0.15, 300); } /* Magenta */Kontrast und Barrierefreiheit (WCAG)
Die Web Content Accessibility Guidelines (WCAG 2.1) definieren Mindest-Kontrastanforderungen: Normaler Text braucht ein Kontrastverhältnis von mindestens 4,5:1 (AA) oder 7:1 (AAA). Großer Text (18px+ bold oder 24px+ regular) braucht 3:1 (AA) oder 4,5:1 (AAA).
Das Kontrastverhältnis wird über die relative Luminanz berechnet: (L1 + 0.05) / (L2 + 0.05), wobei L1 die hellere und L2 die dunklere Farbe ist. Die relative Luminanz berücksichtigt, dass das menschliche Auge für Grün empfindlicher ist als für Rot oder Blau: L = 0.2126×R + 0.7152×G + 0.0722×B (nach Linearisierung).
WCAG 3.0 (in Entwicklung) wird den neuen APCA-Algorithmus (Accessible Perceptual Contrast Algorithm) verwenden, der die Leserichtung berücksichtigt: Dunkle Schrift auf hellem Hintergrund braucht andere Werte als helle Schrift auf dunklem Hintergrund. APCA liefert genauere Ergebnisse für reale Lesbarkeit.
Praktischer Tipp: Teste Kontraste nicht nur für den Standardzustand, sondern auch für Hover, Focus, Disabled-Zustände und vor Bildhintergründen. Verwende keinen reinen Schwarz-auf-Weiß-Kontrast (#000 auf #FFF) — das ist zu hart für längeres Lesen. #1a1a1a auf #fafafa ist angenehmer und erfüllt trotzdem AAA.
// Kontrastverhältnis nach WCAG 2.1 berechnen
function relativeLuminance(r, g, b) {
const [rs, gs, bs] = [r, g, b].map(c => {
c = c / 255;
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
});
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
}
function contrastRatio(color1, color2) {
const l1 = relativeLuminance(...color1);
const l2 = relativeLuminance(...color2);
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
}
// Beispiel: Dunkelblau auf Weiß
const ratio = contrastRatio([26, 54, 93], [255, 255, 255]);
// 11.8:1 — erfüllt AAA
// WCAG-Level prüfen
function getWCAGLevel(ratio, isLargeText = false) {
if (isLargeText) {
if (ratio >= 4.5) return 'AAA';
if (ratio >= 3) return 'AA';
} else {
if (ratio >= 7) return 'AAA';
if (ratio >= 4.5) return 'AA';
}
return 'Fail';
}Farbpaletten programmatisch generieren
Ein Design-System braucht konsistente Farbskalen: 10 Abstufungen pro Farbe (50, 100, 200, ... 900), die gleichmäßig von hell nach dunkel verlaufen. In HSL erreichst du das durch lineare Variation der Lightness — aber das Ergebnis sieht nicht gleichmäßig aus. In OKLCH funktioniert es tatsächlich.
Komplementärfarben liegen 180° gegenüber auf dem Farbkreis. Triadische Farben sind 120° voneinander entfernt. Analoge Farben liegen 30° nebeneinander. Diese Beziehungen funktionieren in HSL und OKLCH gleich (Hue-Rotation), aber die wahrgenommene Harmonie ist in OKLCH besser, weil die Helligkeiten konsistent sind.
Für Dark Mode: Invertiere nicht einfach die Farben. Stattdessen erstelle separate Lightness-Skalen. Im Light Mode geht die Skala von L=0.95 (Hintergrund) bis L=0.25 (Text). Im Dark Mode von L=0.15 (Hintergrund) bis L=0.90 (Text). Die Chroma-Werte können im Dark Mode leicht reduziert werden, da gesättigte Farben auf dunklem Hintergrund grell wirken.
CSS Custom Properties mit OKLCH machen das elegant: Definiere --surface-l und --text-l als Variablen die per Klasse (.dark) umgeschaltet werden. Alle Farben referenzieren diese Variablen. Ein einziger Toggle schaltet das gesamte Farbsystem um, ohne hunderte Farbwerte manuell anzupassen.
// Farbskala programmatisch generieren (OKLCH)
function generateScale(hue, chroma = 0.15, steps = 9) {
const scale = [];
for (let i = 0; i < steps; i++) {
// Lightness von 0.95 (hell) bis 0.15 (dunkel)
const lightness = 0.95 - (i * 0.1);
// Chroma ist in der Mitte am höchsten
const c = chroma * Math.sin((i / (steps - 1)) * Math.PI);
scale.push(`oklch(${lightness.toFixed(2)} ${c.toFixed(3)} ${hue})`);
}
return scale;
}
// Blaue Skala generieren
const blueScale = generateScale(240);
// ["oklch(0.95 0.000 240)", ..., "oklch(0.55 0.150 240)", ..., "oklch(0.15 0.000 240)"]
// Komplementär- und Triadenfarben
function complementary(hue) { return (hue + 180) % 360; }
function triadic(hue) { return [(hue + 120) % 360, (hue + 240) % 360]; }
function analogous(hue) { return [(hue + 30) % 360, (hue - 30 + 360) % 360]; }Farben in JavaScript: Konvertierung und Manipulation
Die häufigste Aufgabe: Hex nach RGB und zurück. #3498db → r=52, g=152, b=219 (parseInt auf je 2 Hex-Zeichen). RGB nach Hex: Jede Komponente in Hex konvertieren und auffüllen (.toString(16).padStart(2, "0")). Für HSL-Konvertierung brauchst du etwas mehr Mathematik.
RGB zu HSL: Normalisiere R, G, B auf [0,1]. Finde Max und Min. H hängt davon ab welcher Kanal maximal ist (unterschiedliche Formeln für R, G, B). S = (Max-Min)/(1-|2L-1|). L = (Max+Min)/2. Die Formeln sind fehleranfällig bei Edge Cases (Grautöne wo S=0, Schwarz/Weiß).
Für produktiven Code: Verwende eine Bibliothek statt eigener Konvertierung. culori (4 KB, tree-shakeable) ist der moderne Standard für Farbmanipulation in JavaScript. Ältere Alternativen: chroma.js (größer, mehr Features) oder color (einfaches API). Alle unterstützen OKLCH.
CSS bietet seit 2024 auch native Farbmanipulation: color-mix(in oklch, var(--primary), white 30%) mischt 30 % Weiß in die Primärfarbe — direkt in CSS ohne JavaScript. Relative Farbsyntax erlaubt noch mehr: from var(--primary) oklch(calc(l + 0.1) c h) macht die Farbe heller.
// Hex ↔ RGB Konvertierung
function hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
} : null;
}
function rgbToHex(r, g, b) {
return '#' + [r, g, b]
.map(x => x.toString(16).padStart(2, '0'))
.join('');
}
// RGB zu HSL
function rgbToHsl(r, g, b) {
r /= 255; g /= 255; b /= 255;
const max = Math.max(r, g, b), min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
case g: h = ((b - r) / d + 2) / 6; break;
case b: h = ((r - g) / d + 4) / 6; break;
}
}
return { h: Math.round(h * 360), s: Math.round(s * 100), l: Math.round(l * 100) };
}Praxis-Tipps: Farben im Entwickleralltag
CSS Custom Properties für Farben: Definiere Farben immer als Custom Properties auf :root — nie als Hex-Werte direkt in Komponenten. Das ermöglicht Theming, Dark Mode und konsistente Änderungen an einer Stelle. Benenne semantisch (--color-surface, --color-text-primary) statt visuell (--light-blue).
Transparenz über Alpha-Kanal: Verwende rgb(0 0 0 / 10%) statt opacity: 0.1 wenn nur die Farbe transparent sein soll, nicht das ganze Element inklusive Kinder. Für Overlays und Schatten ist Alpha-Transparenz fast immer die richtige Wahl.
Farbblindheit berücksichtigen: 8 % der Männer sind rot-grün-farbblind (Deuteranopie/Protanopie). Verlasse dich nie allein auf Farbe um Information zu transportieren — verwende zusätzlich Icons, Labels oder Muster. Teste dein UI mit Simulationstools (Chrome DevTools > Rendering > Emulate vision deficiencies).
Performance-Hinweis: color-mix() und relative Farbsyntax in CSS werden vom Browser zur Paint-Zeit berechnet — nicht vorab. In Animationen mit vielen Farb-Interpolationen kann das messbar sein. Für Performance-kritische Animationen berechne die Zielfarben einmal und verwende feste Werte.