SELF-Treffen in Mannheim 2025

SELFHTML wird 30 Jahre alt!
Die Mitgliederversammlung findet am 24.05.2025 um 10:00 statt. Alle Mitglieder und Interessierte sind herzlich eingeladen.
Davor und danach gibt es Gelegenheiten zum gemütlichen Beisammensein. → Veranstaltungs-Ankündigung.

Farbe/Relative Farbangaben

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Bisher gab es nur feste Farbangaben, z.B. ein RGB-Wert für den Hintergrund. Wenn man eine dunklere Schattierung benötigte, musste dies in einem Grafikprogramm oder Farbwähler ermittelt und dann wieder als fester Wert eingegeben werden.

Dieses Tutorial zeigt, wie Du dies nun bequem mit den neuen Funktionen des CSS Color Module Level 5[1] nur im CSS erledigen kannst!

Achtung!

Die Beispiele in diesem Tutorial funktionieren (Stand: Juli 2024) in allen modernen Browsern! Farben außerhalb des RGB-Farbraums werden in einigen hochwertigen Geräten wie iPhones ab 2017 angezeigt, sonst im kleineren RGB-Farbraum dargestellt.

Es gibt einen Polyfill von Evil Martians, alternativ müsste man die errechneten Farbwerte einmal manuell in HEX-Werte umrechnen.

--Matthias Scharwies (Diskussion) 13:11, 11. Juli 2024 (CET)

relative Farbangaben

Bisher mussten alle Farben einer Farbpalette in einem Grafikprogramm berechnet und festgelegt werden. So kamen für eine Grund- und mehrere Akzentfarben schnell viele Farben zusammen.

Mit der relativen Farbsyntax[2] können Grund- und Akzentfarben in einem beliebigen Farbraum oder einer beliebigen Syntax und entsprechende Varianten mit viel weniger Code erstellt werden.[3]

Schlüsselwort from

In Level 5 der Color-Spezifikation wurden alle Farbfunktionen um die Möglichkeit erweitert, Farben relativ zu einer Bezugsfarbe angeben zu können. Dazu notiert man das Schlüsselwort from, die Bezugsfarbe und dann die Farbwerte. Die Besonderheit ist, dass man für diese Farbwerte die calc()-Funktion verwenden kann, um aus den Farbanteilen der Bezugsfarbe eine neue Farbe zu errechnen. Diese Farbanteile werden durch die Buchstaben repräsentiert, die das jeweilige Farbmodell verwendet, der Transparenzwert der Bezugsfarbe kann mit dem Schlüsselwort alpha einbezogen werden.

In diesem Beispiel aus der Spec wird eine Farbe um eine durchscheinende Variante für ein Overlay erweitert:

:root { --bg-color: blue; }
.overlay {
  background: rgb(from var(--bg-color) r g b / 50%);
}
var(--bg-color)
rgb(from var(--bg-color) r g b / 50%)

Als Standard-Hintergrundfarbe wird Blau in der custom property --bg-color:blue festgelegt.

Für die Klasse overlay wird nun eine rgb()-Funktion aufgerufen, die über das Schlüsselwort from die entsprechende Farbfestlegung aufnimmt. Die r-, g- und b-Kanäle der Ursprungsfarbe bleiben unverändert, indem sie mit den Schlüsselwörtern angegeben werden, die ihre Werte von der Ursprungsfarbe ableiten. Zusätzlich wird aber die Deckkraft auf 50 % gesetzt, um sie transparent zu machen, unabhängig davon, wie hoch die Deckkraft der Ursprungsfarbe war.

Beachten Sie: Die Bezugsfarbe kann in einem beliebigen Farbmodell angegeben sein, CSS rechnet das passend um. Das kann natürlich zu Genauigkeitsverlusten führen: wenn Sie eine OKLCH-Farbe, die außerhalb des SRGB-Farbraums liegt, als Bezugsfarbe in der RGB()-Funktion verwenden, entsteht bei der Umrechnung eine möglichst gut passende Ersatzfarbe.

Rechenoperationen

Nehmen wir an, Sie möchten zu einer leuchtenden Umrissfarbe eine abgedunkelte Füllfarbe haben. Das mussen Sie früher manuell machen, jetzt können Sie es mit Hilfe einer relativen Farbangabe errechnen.

:root { --umrissfarbe: green; }
svg rect {
   stroke: var(--umrissfarbe);
   fill: oklch(from var(--umrissfarbe) calc(l / 2) c h);
}
var(--umrissfarbe)
oklch(from var(--umrissfarbe) calc(l / 2) c h)

In diesem Beispiel wird die Umrissfarbe abgedunkelt, indem ihre Helligkeit halbiert wird, ohne irgendeinen anderen Aspekt der Farbe zu verändern. Die Umrissfarbe ist zwar als Farbname (also sRGB) angegeben, das wird von CSS aber automatisch in das Farbmodell der genutzten Farbfunktion umgerechnet.

Wir lesen die Umrissfarbe mit var(--umrissfarbe) aus ihrem custom property aus und setzen sie damit als Bezugsfarbe für from. Für die neue Farbe können wir die Farbwerte l (Luminanz), c (Chroma) und h (Farbton) des LCH-Farbmodells verwenden. Um eine Rechenformel benutzen zu können, benötigt CSS die calc()-Funktion, womit wir für die neue Farbe die halbe Luminanz errechnen. Chroma und Farbton werden unverändert übernommen, dafür ist calc() nicht notwendig.

Man könnte das auch mit der hsl()-Funktion machen, aber diese Funktion berücksichtigt nicht die physiologischen Eigenschaften des menschlichen Auges. Das L*ab-Farbmodell (bzw. die Polarkoordinatenform Lch) hingegen schon, für den gewünschten Effekt der halben Helligkeit ist es gleichgültig, welche Form wir verwenden.

Farbänderung

Oftmals ergibt sich auch der Wunsch, den Ton einer Farbe abzuändern, Sättigung und Helligkeit aber beizubehalten. Auch hierfür können wir das Polarkoordinatenmodell des OKLCH-Farbraums nutzen.

:root { --bg-color: lime; }
.error {
  -background: oklch(from var(--bg-color) calc(l * 0.5) c 30);
}
var(--bg-color)
oklch(from var(--bg-color) calc(l * 0.5) c 30)

Die Hintergrundfarbe ist ein knalliges Grün, für Fehlermeldungen, wird der Farbton (Hue) auf einen Wert von 30(°) gesetzt.

Wenn Sie zu einer Farbe die Komplementärfarbe errechnen wollen, können Sie das ganz einfach tun, indem Sie 180 auf den h-Wert aufaddieren. Die Farbtonwerte der diversen Polarkoordinatenmodelle folgen dem gebräuchlichen Farbkreis.

Beachte: Wichtiges wie Fehlermeldungen sollte nicht alleine durch Farben gekennzeichnet werden!

eine eigene Farbpalette erstellen

Dies wollen wir nun für eine eigene Farbpalette nutzen:

Ausgangspunkt sind unsere SELF-Farben Gelb und Blau, die durch einen Rot-Ton als Signalfarbe ergänzt werden sollen:

oklch(50% 0.1 240) SELF-Blau
oklch(50% 0.16 90)
oklch(75% 0.16 90)   SELF-Gelb
oklch(50% 0.16 30)   Rot als Akzent für Warnungen
oklch(50% 0.16 120) Grün als Akzent für Tipps und Empfehlungen


:root {
  --brand-color: oklch(50% 0.1 240);
  --accent1-clr: oklch(75% 0.16 90);
  --accent2-clr: oklch(50% 0.16 30);
  --accent3-clr: oklch(50% 0.16 120);
}

Das Blau soll nun in Schattierungen aufgehellt und abgedunkelt werden:

:root {
  --brand-color-darkest:  oklch(from var(--brand-color) 16% c h);
  --brand-color-darker:   oklch(from var(--brand-color) 28% c h);
  --brand-color-dark:     oklch(from var(--brand-color) 40% c h);
  --brand-color:          oklch(50% 0.1 240);
  --brand-color-light:    oklch(from var(--brand-color) 70% c h);
  --brand-color-lighter:  oklch(from var(--brand-color) 82% c h);
  --brand-color-lightest: oklch(from var(--brand-color) 93% c h);
}
oklch(from var(--brand-color) 16% c h)
oklch(from var(--brand-color) 28% c h)
oklch(from var(--brand-color) 40% c h)
oklch(50% 0.1 240)
oklch(from var(--brand-color) 70% c h)
oklch(from var(--brand-color) 82% c h)
oklch(from var(--brand-color) 94% c h)

Ergebnis

Schöner wäre es, wenn die Schattierung als custom property auch anderen Farben zur Verfügung stehen würde:

  --dk2: -.30;
  --dk1: -.16;
  --dk:  -0.08;
  --li2:  0.5;
  --li1:  0.35;
  --li:   0.2;
oklch(from var(--blu) calc(l + var(--dk2)) c h)
oklch(from var(--blu) calc(l + var(--dk1)) c h)
oklch(from var(--blu) calc(l + var(--dk)) c h)
oklch(50% 0.1 240)
oklch(from var(--blu) calc(l + var(--li)) c h)
oklch(from var(--blu) calc(l + var(--li1)) c h)
oklch(from var(--blu) calc(l + var(--li2)) c h)
oklch(from var(--yel) calc(l + var(--dk2)) c h)
oklch(from var(--yel) calc(l + var(--dk1)) c h)
oklch(from var(--yel) calc(l + var(--dk)) c h)
oklch(50% 0.16 90)
oklch(from var(--yel) calc(l + var(--li)) c h)
oklch(from var(--yel) calc(l + var(--li1)) c h)
oklch(from var(--yel) calc(l + var(--li2)) c h)
oklch(from var(--red) calc(l + var(--dk2)) c h)
oklch(from var(--red) calc(l + var(--dk1)) c h)
oklch(from var(--red) calc(l + var(--dk)) c h)
oklch(50% 0.16 30)
oklch(from var(--red) calc(l + var(--li)) c h)
oklch(from var(--red) calc(l + var(--li1)) c h)
oklch(from var(--red) calc(l + var(--li2)) c h)
oklch(from var(--gre) calc(l + var(--dk2)) c h)
oklch(from var(--gre) calc(l + var(--dk1)) c h)
oklch(from var(--gre) calc(l + var(--dk)) c h)
oklch(50% 0.16 120)
oklch(from var(--gre) calc(l + var(--li)) c h)
oklch(from var(--gre) calc(l + var(--li1)) c h)
oklch(from var(--gre) calc(l + var(--li2)) c h)

Die Signalfarbe Gelb soll zusätzlich zu den unterschiedlichen Helligkeitsstufen auch eine andere Chroma erhalten:

:root {
  --yel-dk1: oklch(28% 0.1  90);
  --yel-dk:  oklch(40% 0.15 90);
  --yel:     oklch(75% 0.2  90);
  --yel-li:  oklch(82% 0.15 90);
  --yel-li1: oklch(89% 0.1  90);
  --yel-li2: oklch(96% 0.05 90);
}
oklch(28% 0.1 90)
oklch(40% 0.15 90)
oklch(75% 0.2 90)
oklch(82% 0.15 90)
oklch(89% 0.1 90)
oklch(96% 0.05 90)

Textfarben mit hohem Kontrast

Früher musst man im nächsten Schritt zu jeder Hintergrundfarbe passende Schriftfarben suchen und dann den Kontrast testen. Jetzt schafft dies CSS ganz alleine:

Alle Grundfarben unserer Palette haben die gleiche wahrgenommene Helligkeit.

Im folgenden Beispiel werden für dunkle Farbtöne helle Schriftfarben, für helle Hintergründe entsprechend dunklere Varienten erzeugt:[4]

.info {
	color: var(--color-primary);
	background-color: oklch(from var(--color-primary) calc(l + 45) calc(c *0.5) h);	
	h2 {
		background-color: var(--color-primary);
		color: oklch(from var(--color-primary) calc(l + 40) c h);
	}
}

.warning {
	color: var(--color-secondary);
	background-color: oklch(from var(--color-secondary) calc(l + 45) c h);
	h2 {
		background-color: var(--color-secondary);
		color: oklch(from var(--color-secondary) calc(l + 60) c h);
	}	
}

button {
	background-color: var(--color-accent);
	color: oklch(from var(--color-accent) calc(l - 50) c h);
}
kontrastreiche Schriftfarben mit relativer Farbsyntax ansehen …
Beachte: Aktuell (Stand: Juli 2024) können alle modernen Browser diese Farbangaben verstehen.

color-mix() als Fallback

Nutzer älterer Browser sehen bei den oben gezeigten Farbfeldern mit relativen Farbangaben nur weiße Kästchen.

Mit der color-mix()-Funktion kann man dies umgehen:

color-mix(method, color1[ p1], color2[ p2])

Das Schlüsselwort in leitet einen Farbraum ein, in dem die Farbmischung erzeugt werden soll.

Danach folgen zwei Farbangaben, optional ergänzt durch eine Prozentangabe:

color-mix(in oklch, red, yellow)
color-mix(in oklch, red 30%, blue )
color-mix(in srgb, #337599 80%, white)
color-mix(in oklch longer hue, #337599 60%, white)


[5]

Farbkreis in oklch() ansehen …

Farbinterpolation

Zwischen zwei Farben einen Übergang zu ermitteln – zu interpolieren – ist eine Aufgabe, die CSS an verschiedenen Stellen lösen muss.

Das CSS Farbenmodul Level 4 definiert eine allgemeine Syntax, um den bei der Farbinterpolation zu verwendenden Farbmodell festlegen zu können. Eine solche Interpolationsangabe wird durch das Schlüsselwort in eingeleitet, gefolgt von dem konkreten Farbkoordinatensystem, das verwendet werden soll. Bei den Systemen, die auf Polarkoordinaten basieren, kann zusätzlich die Richtung angegeben werden, um die der Farbwert gedreht werden soll.

Syntax

  in <system> [ <richtung> hue ]

Bei in und hue handelt es sich um Schlüsselwörter. Als <system> ist eins der nachfolgend aufgelisteten Koordinatensysteme zu verwenden. Die möglichen Werte für <richtung> finden sich bei den Polarkoordinatensystemen.

Rechtwinklige Koordinatensysteme

Die Koordinaten in diesen Darstellungen haben in jeder Dimension zwei klar definierte, entgegengesetzte Enden.

srgb
Das ursprünglich verwendete Verfahren ist eine lineare Interpolation im sRGB-Farbraum. Diese Interpolation ist einfach realisierbar, aber der sRGB Farbraum ist nicht auf gleichförmige Ausleuchtung oder gleichmäßige Wahrnehmbarkeit ausgelegt, sondern auf einfache technische Darstellung auf einem Monitor. Deshalb sind sRGB-Farbverläufe oft zu dunkel oder zu sehr grau.
srgb-linear
xyz
xyz-d50
xyz-d65
Diese Farbräume haben eine lineare Lichtintensität und führen zu einem Ergebnis, das der Mischung farbigen Lichts entspricht. xyz steht für den XYZ-Farbraum im CIE Normvalenzsystem, von dem die übrigen CIE-Farbräume wie Lab oder LCH abgeleitet sind.
lab
oklab
L*a*b ist eine verständlichere Darstellung des CIE Normfarbraumes, basierend auf L (Helligkeit) und einer Positionierung auf den Komplementärfarbskalen grün/rot sowie blau/gelb. Der L*a*b Farbraum soll besser der menschlichen Wahrnehmung entsprechen, macht aber Schwierigkeiten bei der Bildverarbeitung, weshalb von Björn Ottoson eine verbesserte Umrechnung von XYZ in Lab vorgeschlagen wurde, die beim W3C Anklang fand. Dieses bessere Lab ist "okay", deshalb nannte man es oklab. In einer Farbinterpolation haben lab und oklab den Vorteil, dass der wahrgenommene Farbübergang gleichmäßiger ist.

Polare Koordinatensysteme

In einem polaren Koordinatensystem ist eine Achse kreisförmig, d.h. ihr Ende geht nahtlos wieder in den Anfang über

hsl
hwb
Das System HSL (Hue - Saturation - Lightness = Farbton - Sättigung - Helligkeit) verwendet ein Farbenrad für den Farbton und leitet die übrigen Farben als Farbsättigung (0%=Grau, 100%=Vollton) und Helligkeit (von 0=Schwarz, 50%=Vollfarbe und 100%=Weiß) daraus ab. Das HWB System (Hue - Whiteness - Blackness) arbeitet ähnlich. Der Vorteil dieser Systeme ist, dass sie Menschen verständlicher sein sollen als RGB-Farbangaben. Bei einer Interpolation kann man Farbübergänge als Wechsel zwischen den Farbtönen darstellen. Das führt bei einem Übergang rot nach blau aber auch zu ganz anderen Farben, die eher ein Spektrum als einen Farbübergang bilden.
lch
oklch
Der LCH Farbraum ist eine auf Polarkoordinaten umgerechnete Version des L*a*b-Farbraums und basiert auf Helligkeit (L=Luminanz), Farbsättigung (C=Chroma) und Farbton (H=Hue). Der Farbton ist dabei der, der auch im HSL-System genutzt wird. LCH gilt ebenfalls als gleichförmig in der Wahrnehmung, und soll bei Farbinterpolationen das „Ausgrauen“ der Farben vermeiden.

Interpolationsrichtung des Farbtons

In einem polaren Koordinatensystem stellt sich die Frage, in welcher Richtung man sich bei einer Interpolation des Farbtons um den Mittelpunkt des Farbrades drehen möchte. Der Übergang von H=100° zu H=200° kann aus der Folge 100, 101, 102, ... 199, 200 bestehen, aber auch aus der Folge 100, 99, ..., 1, 0, 359, 358, ... 201, 200.

Standardmäßig verwendet CSS den kleineren möglichen Drehwinkel. Das entspricht der Richtungsangabe shorter in der Farbinterpolationsangabe. Den größeren Drehwinkel spezifiziert man mit der Angabe longer.

Darüber hinaus gibt es noch die Angaben increasing (aufsteigend) und decreasing (absteigend), damit gibt man an, dass man in Richtung auf- oder absteigender Winkelwerte interpolieren möchte, ganz gleich, ob das der größere oder der kleinere Winkel ist.

Vergleichstafel

Die nachfolgende Vergleichstafel zeigt, wie sich die Interpolationsmethode auswirkt (in Arbeit!)

Markup für die Vergleichstafel ansehen …
<style>
ul {
   --from: hsl(340, 100%, 50%);
   --to: hsl(130, 100%, 50%);
}
li {
   height: 2em;
   background: linear-gradient(to right var(--hue-scheme), var(--from), var(--to)) right center / 85% 100% no-repeat;
}
</style>

<ul>
  <li style="--hue-scheme: in srgb">SRGB</li>
  ...
  <li style="--hue-scheme: in oklch longer hue">OKLCH longer hue</li>
</ul>


Anhang: Hue-Rad

60°
120°
240°
300°
180°

Weblinks

  1. CCSSWG: CSS Color Module Level 5
  2. CCSSWG: Relative Color Syntax (CSS Color Module Level 5)
  3. CSS-Syntax für relative Farben (developer.chrome.com)
  4. Farbe kontrastieren (developer.chrome.com)
  5. Simplify Your Color Palette With CSS Color-Mix() (smashing magazine)