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 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.

Rechenoperationen

:root { --bg-color: green; }
.foo {
  --darker-accent: oklch(from var(--bg-color) calc(l / 2) c h);
}
var(--bg-color)
oklch(from var(--bg-color) calc(l / 2) c h)

In diesem Beispiel wird die Ursprungsfarbe abgedunkelt, indem ihre Helligkeit halbiert wird, ohne irgendeinen anderen Aspekt der Farbe zu verändern.

Dabei wird die vorher festgelegte custom property --bgcolor durch from als Basisfarbe festgelegt. Während der 2. und 3, Parameter c und h unverändert bleiben, wird die Hellligkeit l durch eine calc()-Funktion verändert.

Beachte: Die Ursprungsfarbe ist ein Farbname (und damit sRGB), wird aber automatisch als OKLCH-Farbe interpretiert, da sie in der oklch()-Funktion verwendet wird.

Farbänderung

: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.

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)