Dynamische Farbgenerierung: Algorithmen für Design-Systeme
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.
Design-Systeme generieren ihre Farbpaletten zunehmend programmatisch, anstatt jeden Farbton manuell auszuwählen. Eine einzige gut gewählte Markenfarbe kann ein gesamtes System begründen: eine vollständige tonale Skala, zugängliche Textpaarungen, eine Dunkelmodus-Variante und harmonische Begleitfarben. Die zugrunde liegenden Algorithmen entscheiden darüber, ob das Ergebnis professionell gestaltet oder rechnerisch steif wirkt.
Dieses Tutorial erklärt die Kernalgorithmen hinter der dynamischen Farbgenerierung — Schattenskalen, kontrastbewusste Auswahl, Theme-Ableitung, automatischer Dunkelmodus und die Bibliotheken, die diese Techniken implementieren — mit praktischen Beispielen, die du auf dein eigenes Design-System anwenden kannst.
Algorithmen zur Schattengenerierung
Eine Schattenskala ist eine systematische Abfolge tonaler Varianten einer einzelnen Farbe: vom nahezu Weißen am hellen Ende bis zum nahezu Schwarzen am dunklen Ende. Tailwind CSS' Skala von 50 bis 950 ist das bekannteste Beispiel. Die Qualität einer Schattenskala hängt vollständig vom verwendeten Algorithmus ab.
Das Problem mit HSL-basierter Schattengenerierung
Der naive Ansatz — von einer HSL-Farbe ausgehend und die Helligkeit in gleichmäßigen Schritten erhöhen — erzeugt ungleichmäßig wirkende Skalen. Da die Helligkeitsachse von HSL nicht perzeptuell gleichmäßig ist, sieht ein Schritt von L=40 zu L=50 je nach Farbton unterschiedlich aus. Gelb verschiebt sich dramatisch; Blau bewegt sich kaum. Die resultierende Skala wirkt handgefertigt und inkonsistent.
// Naiver Ansatz — so NICHT vorgehen
function hslShades(hue, saturation, steps = 10) {
return Array.from({ length: steps }, (_, i) => {
const lightness = 5 + (i / (steps - 1)) * 90;
return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
});
}
Das Ergebnis wirkt für mittlere Gelb- und Grüntöne akzeptabel, verschlechtert sich jedoch stark bei Blau, Violett und entsättigten Farbtönen.
OKLCH-basierte Schattengenerierung
Der richtige Ansatz verwendet OKLCH, wo die L-Achse perzeptuell kalibriert ist. Gleiche L-Schritte erzeugen unabhängig vom Farbton gleichmäßig wirkende Stufen. Der Algorithmus lautet:
- Wandle die Basis-Markenfarbe in OKLCH um
- Definiere den Helligkeitsbereich (z. B. L = 0,97 bei Stufe 50, bis L = 0,15 bei Stufe 950)
- Interpoliere die Chroma zusammen mit der Helligkeit — sehr helle und sehr dunkle Stufen haben von Natur aus eine geringere Chroma als mittlere Töne
- Halte den Farbton konstant (oder wende eine kleine Farbton-Rotation für ästhetische Warm-/Kälteveränderungen an)
- Wandle jeden OKLCH-Punkt zurück in HEX oder sRGB um
// OKLCH-basierte Schattengenerierung
function oklchShades(baseHex) {
const { L, C, H } = hexToOklch(baseHex);
// Helligkeitsstufen für eine 11-stufige Skala (50–950)
const lightnessStops = [0.97, 0.93, 0.86, 0.78, 0.68, 0.58, 0.48, 0.39, 0.30, 0.22, 0.15];
// Chroma-Kurve: gipfelt bei der Chroma der Basisfarbe, läuft zu den Extremen hin aus
const chromaStops = lightnessStops.map(l => {
const distance = Math.abs(l - L);
const falloff = Math.max(0, 1 - distance * 1.5);
return C * falloff;
});
return lightnessStops.map((l, i) => ({
oklch: `oklch(${l} ${chromaStops[i].toFixed(3)} ${H})`,
hex: oklchToHex(l, chromaStops[i], H),
}));
}
Die Chroma-Abfallkurve ist der wichtigste Abstimmungsparameter. Volle Chroma am dunklen Ende zu erlauben erzeugt Töne, die unnatürlich gesättigt und dunkel wirken. Die Chroma zum hellen Ende hin auszublenden vermeidet ausgewaschene Pastelltöne mit zu wenig Farbcharakter. Die genaue Kurvenform ist eine Designentscheidung — einige Systeme verwenden eine quadratische Kurve, andere eine stückweise lineare Funktion.
Verwende den Shade Generator, um diesen Algorithmus auf jede Markenfarbe anzuwenden. Er erzeugt eine Tailwind-kompatible Skala mit zugänglichen Standardwerten.
Farbton-Rotation für Temperaturverschiebungen
Viele Design-Systeme wenden eine kleine Farbton-Rotation zusammen mit der Helligkeit an — hellere Stufen verschieben sich leicht wärmer (niedrigere H-Werte in Richtung Gelb), dunklere Stufen verschieben sich leicht kühler (höhere H-Werte in Richtung Blau/Violett). Dies ahmt nach, wie sich physische Materialien tatsächlich verhalten: Ein dunkles Marineblau wirkt in seinen hellen Tönen leicht wärmer.
// Subtile Farbton-Rotation hinzufügen
function shadeWithHueShift(L_target, C_target, H_base, L_base) {
const lightnessDelta = L_target - L_base;
// Bis zu ±10 Grad verschieben: warm (−) für helle Stufen, kühl (+) für dunkle
const hueShift = -lightnessDelta * 15;
return { L: L_target, C: C_target, H: H_base + hueShift };
}
Die Stärke der Farbton-Verschiebung ist eine stilistische Entscheidung. Tailwinds eigene Palette wendet subtile Farbton-Verschiebungen an. Lineare Skalen (ohne Verschiebung) wirken sauber, können aber mechanisch erscheinen.
Kontrastbewusste Farbauswahl
Eine schöne Schattenskala zu generieren ist nur die halbe Aufgabe. Damit jede Stufe als Textfarbe oder Hintergrund nutzbar ist, musst du wissen, welche Stufe der Skala du für zugänglichen Kontrast damit paaren kannst.
Algorithmus: Die Mindestkontrastpassierende Stufe finden
Gegeben eine Hintergrundfarbe und eine Schattenskala, finde die dunkelste Stufe auf der Skala, die WCAG AA (4,5:1 für normalen Text) besteht:
function findAccessibleTextShade(backgroundHex, shadeScale) {
const bgLuminance = relativeLuminance(backgroundHex);
for (const shade of shadeScale) {
const shadeLuminance = relativeLuminance(shade.hex);
const ratio = contrastRatio(bgLuminance, shadeLuminance);
if (ratio >= 4.5) {
return shade; // Erste Stufe, die besteht
}
}
return shadeScale[shadeScale.length - 1]; // Dunkelste verfügbare
}
function contrastRatio(L1, L2) {
const lighter = Math.max(L1, L2);
const darker = Math.min(L1, L2);
return (lighter + 0.05) / (darker + 0.05);
}
Für die meisten praktischen Markenfarben besteht die Stufe 700 oder 800 der Skala AA gegen Weiß, und die Stufe 50 oder 100 besteht AA gegen die dunkelste Stufe der Skala.
Zugängliche Text-Tokens generieren
Ein vollständiges zugängliches Token-System paart jede Hintergrundstufe mit mindestens einer Vordergrundstufe, die WCAG AA besteht. Das gängige Muster:
function generateAccessiblePairs(shadeScale) {
const pairs = {};
shadeScale.forEach((shade, index) => {
// Passende Textfarben für diesen Hintergrund finden
const passingShades = shadeScale.filter(textShade => {
const ratio = contrastRatio(
relativeLuminance(shade.hex),
relativeLuminance(textShade.hex)
);
return ratio >= 4.5;
});
pairs[shade.name] = {
background: shade.hex,
textPrimary: passingShades[passingShades.length - 1]?.hex, // Dunkelste passende
textSecondary: passingShades[passingShades.length - 3]?.hex, // Leicht hellere
};
});
return pairs;
}
Für eine blaue Skala, bei der die Markenfarbe #2563EB (blue-600) ist: - blue-50 Hintergrund → primärer Text: blue-900, sekundärer Text: blue-700 - blue-600 Hintergrund → primärer Text: weiß (#FFFFFF) oder blue-50 - blue-900 Hintergrund → primärer Text: weiß (#FFFFFF) oder blue-100
Der Shade Generator zeigt Kontrastverhältnisse neben jeder generierten Stufe an, sodass du sofort erkennen kannst, welche Paarungen funktionieren.
Theme-Generierung aus einer Markenfarbe
Ausgehend von einer einzelnen Markenfarbe — der auf dem Logo und Marketingmaterial eines Unternehmens erscheinenden — kannst du ein vollständiges Design-System-Theme ableiten: primäre, neutrale und semantische Farbrollen.
Schritt 1: Die primäre Skala verankern
Extrahiere die OKLCH-Werte der Markenfarbe und generiere die vollständige Schattenskala wie oben beschrieben. Setze die Markenfarbe als Stufe 600 (oder wo auch immer sie im wahrgenommenen Helligkeitsbereich liegt — typischerweise im Bereich 500–700 für die meisten Markenfarben).
Schritt 2: Eine neutrale Skala ableiten
Die meisten Design-Systeme benötigen ein neutrales Grau, das harmonisch zur Markenfarbe passt. Ein warmes Neutral wird abgeleitet, indem der Farbton der Markenfarbe stark entsättigt wird:
function deriveNeutralScale(brandOklch) {
const { H } = brandOklch;
// Den Markenfarbton bei sehr niedriger Chroma verwenden — gerade genug für eine warme/kühle Wirkung
const neutralBaseChroma = 0.010; // Fast grau, leichter Farbtoneinfluss
return generateOklchShades({
L: 0.55, C: neutralBaseChroma, H: H
});
}
Für eine blaue Marke (#2563EB, Farbton ≈ 260°) generiert dies eine leicht blau-graue neutrale Skala — kühler als reines Grau, harmonierend mit der Markenfarbe. Für eine orangefarbene Marke neigen die Neutraltöne zu wärmer.
Schritt 3: Semantische Rollen zuweisen
Semantische Tokens ordnen die generierten Skalen funktionalen Rollen zu:
:root {
/* Generiert aus der Markenfarbe blau #2563EB */
--color-primary-50: /* blaue Skala 50 */;
--color-primary-600: /* blaue Skala 600 = Markenfarbe */;
--color-primary-900: /* blaue Skala 900 */;
/* Generiert aus dem Markenfarbton + entsättigt (warmes Neutral) */
--color-neutral-50: /* neutrale Skala 50 */;
--color-neutral-900: /* neutrale Skala 900 */;
/* Semantische Zuweisungen */
--color-bg-base: var(--color-neutral-50);
--color-text-primary: var(--color-neutral-900);
--color-action-primary: var(--color-primary-600);
--color-action-primary-hover: var(--color-primary-700);
--color-action-primary-text: #FFFFFF;
}
Schritt 4: Begleitende Akzentfarben generieren
Für eine vollständige Palette möchtest du vielleicht 1–2 Akzentfarben, die mit der Marke harmonieren. Der Palette Generator kann harmonische Beziehungen berechnen — komplementäre (gegenüber auf dem Farbkreis), triadische oder analoge Begleiter.
Programmatisch liefert das Drehen des OKLCH-Farbtons um feste Offsets harmonische Begleiter mit angepasster Helligkeit und Chroma:
function generateCompanions(brandOklch, scheme = 'triadic') {
const { L, C, H } = brandOklch;
const rotations = {
complementary: [180],
triadic: [120, 240],
analogous: [30, -30],
split_complementary: [150, 210],
};
return rotations[scheme].map(rotation => ({
L, C,
H: (H + rotation + 360) % 360
}));
}
Da L und C in OKLCH unabhängig vom Farbton sind, haben so generierte Begleitfarben dieselbe Helligkeit und Chroma wie die Markenfarbe — sie sind automatisch perzeptuell ausgeglichen und erfordern keine manuelle Anpassung.
Automatische Dunkelmodus-Palette
Eine Dunkelmodus-Palette aus einer Hellmodus-Palette zu generieren ist nicht einfach eine Umkehrung der Stufenzuweisungen. Eine Dunkelmodus-Palette erfordert:
- Dunklere Hintergründe (offensichtlich)
- Helleren Text, der dieselben Kontrastverhältnisse wie der Hellmodus-Text beibehält
- Angepasste Primärfarben — mittlere Markenstufen (500–600) sehen auf hellen Hintergründen oft korrekt aus, wirken aber auf dunklen Hintergründen übermäßig hell oder gesättigt
- Reduzierte Chroma in einigen Bereichen — dunkle Oberflächen mit hoher Chroma können aggressiv wirken
Algorithmus: Dunkelmodus-Tokens automatisch generieren
Der einfachste zuverlässige Algorithmus ordnet Hellmodus-Stufenindizes mithilfe einer Spiegelungsfunktion Dunkelmodus-Äquivalenten zu:
// Hellmodus: Hintergrund verwendet helle Stufen, Text verwendet dunkle Stufen
// Dunkelmodus: Hintergrund-/Textbeziehung umkehren
const LIGHT_TO_DARK_MAP = {
// Hintergrundstufen: spiegeln
50: 900, 100: 800, 200: 700,
// Mittlere Stufen: leicht anpassen
300: 700, 400: 600, 500: 500,
600: 400, 700: 300, 800: 200,
// Textstufen: spiegeln
900: 100, 950: 50,
};
function generateDarkTokens(lightTokens, shadeScale) {
return Object.entries(lightTokens).reduce((dark, [token, shade]) => {
const darkShadeIndex = LIGHT_TO_DARK_MAP[shade.index];
dark[token] = shadeScale[darkShadeIndex];
return dark;
}, {});
}
Chroma-Reduktion für dunkle Oberflächen
Dunkle Hintergründe mit voll gesättigten Akzentfarben können störend wirken. Eine praktische Heuristik reduziert die Chroma für Farben, die als Hintergrund- oder Oberflächentoken im Dunkelmodus verwendet werden, erhält sie jedoch für interaktive Farben (Schaltflächen, Links):
function darkSurfaceColor(oklch) {
return {
...oklch,
L: oklch.L * 0.3, // Deutlich dunkler
C: oklch.C * 0.6, // Chroma reduzieren, um Schroffheit zu vermeiden
};
}
function darkInteractiveColor(oklch) {
return {
...oklch,
L: Math.min(0.75, oklch.L + 0.15), // Heller für Sichtbarkeit
C: oklch.C, // Volle Chroma für Lebendigkeit erhalten
};
}
Ausgehend von einem Markenblau #2563EB (≈ oklch(0.55 0.21 264)) zum Beispiel:
- Hellmodus-Aktionsschaltfläche: oklch(0.55 0.21 264) → #2563EB
- Dunkelmodus-Aktionsschaltfläche: oklch(0.70 0.21 264) → ungefähr #60A5FA (blue-400) — heller, um auf dunklen Hintergründen hervorzustechen, ohne Sättigung zu verlieren
Überprüfe die Dunkelmodus-Kontrastverhältnisse mit demselben Algorithmus wie im Hellmodus. Weißer (#FFFFFF) Text auf Dunkelmodus-Oberflächenfarben muss oft durch nahezu weiße Varianten der neutralen Skala der Marke ersetzt werden — --color-neutral-100 oder --color-neutral-50 — anstatt reines Weiß, das auf OLED-Bildschirmen hart wirken kann.
CSS-Implementierung: Dunkelmodus-Variablen
:root {
/* Hellmodus */
--color-bg-base: oklch(0.97 0.01 264);
--color-bg-surface: oklch(1.00 0.00 0);
--color-text-primary: oklch(0.18 0.03 264);
--color-action-primary: oklch(0.55 0.21 264);
}
@media (prefers-color-scheme: dark) {
:root {
/* Automatisch generierter Dunkelmodus */
--color-bg-base: oklch(0.16 0.02 264);
--color-bg-surface: oklch(0.22 0.02 264);
--color-text-primary: oklch(0.93 0.01 264);
--color-action-primary: oklch(0.70 0.21 264);
}
}
Bibliotheken und Werkzeuge
Mehrere ausgereifte Bibliotheken implementieren diese Algorithmen in Produktionsqualität:
Farbgenerierungsbibliotheken
Radix UI Colors — Open-Source, algorithmisch generierte Farbskalen mit automatischem Dunkelmodus und WCAG-getesteten Paarungen. Verwendet einen CIELAB-basierten Algorithmus. Das am besten bewährte Open-Source-System.
npm install @radix-ui/colors
import { blue, blueDark } from '@radix-ui/colors';
// blue.blue9 — das lebendigste Blau, Hellmodus
// blueDark.blue9 — das Äquivalent im Dunkelmodus
Palette.js / Culori — Eine Low-Level-JavaScript-Farbbibliothek mit vollständiger OKLCH-Unterstützung, nützlich für die Entwicklung eigener Algorithmen:
import { oklch, formatHex, interpolate, samples } from 'culori';
const brandOklch = oklch('#2563EB');
const scale = samples(11).map(t => ({
t,
color: formatHex({
mode: 'oklch',
l: 0.15 + t * 0.82, // L von 0,15 (dunkel) bis 0,97 (hell)
c: brandOklch.c * Math.sin(t * Math.PI), // Chroma gipfelt beim mittleren Ton
h: brandOklch.h,
})
}));
ColorBox — Lyfts Open-Source-Tool (ursprünglich von Javier Arce). Generiert zugängliche Farbskalen mit einem kurvenbasierten Helligkeits- und Sättigungseditor. Bietet eine visuelle Benutzeroberfläche für das, was dieses Tutorial algorithmisch beschreibt.
Design-Tool-Integrationen
Figma-Tokens-Plugin — Arbeitet mit Design-Tokens im JSON-Format. Du kannst OKLCH-basierte Tokens definieren und mit CSS-Custom-Properties synchronisieren.
Style Dictionary — Amazons Open-Source-Design-Token-Transformator. Nützlich zum Konvertieren einer Token-Definitionsdatei in CSS-Custom-Properties, SCSS-Variablen, iOS-/Android-Konstanten oder jedes andere Plattformformat.
{
"color": {
"primary": {
"600": { "value": "oklch(0.55 0.21 264)", "comment": "Markenblau" }
}
}
}
Online-Werkzeuge
Der Shade Generator bei ColorFYI wendet OKLCH-bewusste Schattengenerierung auf jede Markenfarbe an und gibt eine Tailwind-kompatible Skala mit Kontrastverhältnissen aus. Der Palette Generator berechnet komplementäre, triadische und analoge Farbharmonien.
Wichtige Erkenntnisse
- HSL-basierte Schattengenerierung erzeugt ungleichmäßige Skalen, weil die Helligkeit in HSL nicht perzeptuell gleichmäßig ist. Gleiche L-Schritte sehen je nach Farbton sehr unterschiedlich aus.
- OKLCH-basierte Schattengenerierung erzeugt durch die kalibrierte L-Achse visuell gleichmäßige Stufen. Füge eine Chroma-Abfallkurve ein — reduziere die Chroma zu den Extremen der Skala hin für natürlich wirkende Töne.
- Kontrastbewusste Token-Generierung paart jede Hintergrundstufe mit der minimalkontrastbestehenden Textstufe mithilfe der WCAG-Relativluminanz-Formel. Überprüfe immer die Verhältnisse, anstatt anzunehmen, dass sie ausreichend sind.
- Theme-Generierung aus einer Markenfarbe erzeugt: (1) eine primäre Skala, die auf der Markenstufe verankert ist, (2) eine harmonisch warme/kühle neutrale Skala mit demselben Farbton bei niedriger Chroma, (3) semantische Token-Zuweisungen und (4) optionale Akzentbegleiter durch Rotation des OKLCH-Farbtons.
- Dunkelmodus-Palettengenerierung kehrt die Stufenrollenzuweisungen um und passt die Chroma an: Oberflächenfarben verwenden reduzierte Chroma, um Schroffheit zu vermeiden; interaktive Farben (Schaltflächen, Links) verwenden erhöhte Helligkeit, um auf dunklen Hintergründen lebendig zu bleiben.
- Bibliotheken wie Radix UI Colors und Culori implementieren diese Algorithmen in Produktionsqualität. Verwende den Shade Generator und den Palette Generator für interaktiven, codefreien Zugang zu denselben Techniken.