Farbtheorie für Entwickler: Hex, RGB, HSL und wann man was nutzt

8 min21. Mai 2026

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.