WCAG 2.2 Farbkontrast: AA treffen ohne Raterei
Ein praktischer Leitfaden zu WCAG 2.2-Kontrastverhältnissen - die AA- und AAA-Schwellenwerte, die Luminanz-Mathematik, warum die Formel fehlerhaft ist und wie APCA das behebt.
Die meisten Kontrastfehler, die ich bei Code-Reviews sehe, sind keine Randfälle. Es ist grauer Platzhaltertext auf Weiß, ein #999 auf #fff, den jemand nach Augenmaß genehmigt und geshipped hat. Die Behebung ist fast immer trivial, sobald man die Zahl kennt, auf die man abzielt. Das ist die Zahl, woher sie kommt und warum die offizielle Mathematik teilweise falsch ist.
Was ein Kontrastverhältnis tatsächlich misst
Ein Kontrastverhältnis ist eine einzelne Zahl, die beschreibt, wie unterschiedlich zwei Farben in der Helligkeit sind, nicht im Farbton. Es geht von 1:1 (identisch, unsichtbar) bis zu 21:1 (reines Schwarz auf reinem Weiß). Alles, was Sie entwerfen, landet irgendwo auf dieser Linie.
Das Schlüsselwort ist Helligkeit, speziell relative Luminanz. Zwei Farben können Ihrem Auge völlig anders vorkommen - ein gesättigtes Rot und ein gesättigtes Blau - und trotzdem fast auf derselben Luminanz sitzen, was geringen Kontrast und unlesbaren Text bedeutet. Der Farbton rettet Sie nicht. Luminanz ist das, um was sich die Formel kümmert, und es ist das, was das Auge verwendet, um feine Details wie Buchstaben aufzulösen.
Relative Luminanz ist eine gewichtete Summe der roten, grünen und blauen Kanäle, nachdem sie von den gamma-kodierten sRGB-Werten, die Ihr CSS verwendet, in lineares Licht umgewandelt wurden. Grün wird stark gewichtet (etwa 0,72), weil das menschliche Auge am empfindlichsten dafür ist; Blau zählt kaum (0,07). Deshalb ist reiner blauer Text auf Schwarz so schwer zu lesen, obwohl Blau sich “hell” anfühlt - es trägt fast keine Luminanz.
Die WCAG 2.2-Schwellenwerte
Hier ist die Tabelle, die es wert ist, als Lesezeichen gespeichert zu werden. Das sind die Erfolgskriterien aus WCAG 2.2, die die Kontrastanforderungen von 2.1 unverändert gelassen haben.
| Element | AA | AAA | SC |
|---|---|---|---|
| Normaler Text | 4,5:1 | 7:1 | 1.4.3 / 1.4.6 |
| Großer Text | 3:1 | 4,5:1 | 1.4.3 / 1.4.6 |
| UI-Komponenten und grafische Objekte | 3:1 | n/a | 1.4.11 |
| Fokus-Indikatoren | 3:1 | n/a | 1.4.11 |
| Logos, deaktivierte Steuerelemente, dekorative Elemente | ausgenommen | ausgenommen | - |
Einige Dinge, die Menschen falsch lesen:
- AAA ist kein “besseres AA, das man immer tun sollte”. Für den Haupttext ist 7:1 mit einem Marken-Farbpalette wirklich schwer zu treffen, und es ist nirgendwo die rechtliche Grundlinie, wo ich ausgeliefert habe. AA ist das Ziel. Streben Sie nach AAA bei Langlese-Oberflächen, wenn Sie können.
- Die 3:1-Zeile für UI-Komponenten ist ein separates Kriterium (1.4.11), und viele Teams vergessen, dass es existiert. Mehr dazu unten.
- “Großer Text” hat eine genaue Definition, und sie ist nicht “sieht groß aus.”
Was als großer Text gilt
Großer Text ist 18 Punkt oder größer, oder 14 Punkt fett oder größer. In CSS-Pixeln kommt das heraus auf:
- 24px und mehr für Regular-Gewicht
- 18,66px und mehr für Fett (700)
Diese 18,66 ist die echte Zahl, nicht 18 oder 19. Sie kommt von 14pt bei der 96dpi-Annahme, die Browser verwenden (14 * 96 / 72 = 18,66). Wenn Ihre fette Überschrift 18px ist, ist sie kein großer Text und benötigt 4,5:1. Ein Pixel spielt hier eine Rolle, und es hat mich bei Audits erwischt.
Nicht-Text-Kontrast: Die Regel, die jeder vergisst
SC 1.4.11 ist diejenige, die Teams aus dem Tritt bringt. Sie besagt, dass jedes nicht-Text-Element, das man wahrnehmen muss, um die Schnittstelle zu nutzen, 3:1 gegen seine benachbarten Farben erreichen muss. Das umfasst:
- Den Rahmen oder die Füllfarbe eines Buttons, Inputs oder Kontrollkästchens - was auch immer seine Grenze visuell definiert
- Icons, die Bedeutung tragen (ein Papierkorb-Icon, ein Status-Punkt, eine Diagrammlinie)
- Fokus-Indikatoren, der Ring oder Umriss, der die Tastaturposition zeigt
- Toggle- und Schieberegler-Zustände
Das klassische Versagen: Ein Such-Input mit einem #e0e0e0 1px-Rahmen auf einer weißen Seite. Dieser Rahmen ist etwa 1,6:1. Ein sehbehinderter Benutzer kann nicht erkennen, wo sich das Feld befindet. Das Anpassen auf ein #767676-ähnliches Grau bringt Sie über 3:1, und das Formular hat plötzlich Struktur.
Fokus-Ringe sind das andere große Problem. Ein schwacher Umriss, der Ihrem Markenblau auf einem blauen Button entspricht, kann unter 3:1 fallen und für Tastaturbenutzer effektiv verschwinden. Geben Sie dem Fokus-Indikator sein eigenes Kontrastbudget, getrennt vom Ruhezustand des Buttons.
Wie das Verhältnis berechnet wird
Die Mathematik ist kurz. Sie nehmen jede Farbe, konvertieren sRGB in lineares Licht, berechnen Luminanz und setzen beide Luminanzwerte in die Verhältnisformel ein. Hier in JS:
// Kanal: 0-255 sRGB-Wert -> lineares Licht 0-1
function linearize(c) {
c = c / 255;
return c <= 0.04045
? c / 12.92
: Math.pow((c + 0.055) / 1.055, 2.4);
}
function relativeLuminance({ r, g, b }) {
const R = linearize(r);
const G = linearize(g);
const B = linearize(b);
// Rec. 709 / sRGB-Luminanzgewichte
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
}
function contrastRatio(fg, bg) {
const L1 = relativeLuminance(fg);
const L2 = relativeLuminance(bg);
const lighter = Math.max(L1, L2);
const darker = Math.min(L1, L2);
return (lighter + 0.05) / (darker + 0.05);
}
Zwei Details, die viel vom Verhalten der Formel erklären:
Der 2.4-Exponent und die 0.04045-Aufteilung sind die sRGB-Übertragungsfunktion. Unterhalb dieser Schwelle ist die Kurve linear (das dunkle Ende), darüber ist es eine Potenzkurve mit Gamma 2.4. Deshalb schwingen kleine numerische Änderungen in dunklen Farben die Luminanz mehr als die gleiche Änderung in hellen Farben.
Der 0.05-Offset, der zu beiden Luminanzwerten hinzugefügt wird, ist der Glanzterm. Er modelliert Umgebungslicht, das von einem Bildschirm abprallt, sodass reines Schwarz nie eine Null-Luminanz erreicht. Ohne ihn würde Schwarz-auf-irgendwas ein viel höheres Verhältnis berechnen. Diese einzelne Konstante ist auch der Ort, an dem die meiste bekannte Ungenauigkeit der Formel lebt.
Warum die Formel wirklich fehlerhaft ist
Die 2.x-Kontrastformel stammt aus den späten 1990ern und war nie dazu gedacht, die Wahrnehmung über den vollen Bereich der Bildschirme zu modellieren, die wir jetzt verwenden. Zwei Versagensmodi tauchen ständig auf.
Sie überschätzt den Dunkelmoduskontrast. Heller Text auf dunklem Hintergrund berechnet routinemäßig ein hohes Verhältnis, während er schlechter gelesen wird als die Zahl vermuten lässt. Der 0.05-Glanzoffset dominiert das dunkle Ende, sodass zwei dunkle Farben, die fast identisch aussehen, ein bestanden-Verhältnis melden können. Ich habe #bbb auf #222 gesehen, das 4,5:1 in einem Checker bestand und sich auf einem echten Laptop-Bildschirm trotzdem schlammig anfühlte.
Es unterschätzt einige Mitteltöne. Bestimmte Grau-auf-Grau- und gesättigte Mittelluminanz-Paare bestehen die Formel nicht, während sie vollkommen lesbar sind, was Designer zu härteren Paletten drängt als notwendig. Die Formel behandelt Kontrast als symmetrisch (Vorder- und Hintergrundfarbe tauschen gibt die gleiche Zahl), aber die menschliche Wahrnehmung von Text ist nicht symmetrisch - dunkler Text auf hellem Hintergrund liest sich anders als das Inverse beim gleichen berechneten Verhältnis.
Also ist die Zahl eine nützliche Leitplanke, keine Grundwahrheit. 4,5:1 zu bestehen ist ein gutes Zeichen. Es ist keine Garantie, und ein knapper Durchgang im Dunkelmodus verdient einen echten menschlichen Blick.
APCA und die WCAG 3-Zukunft
Der Ersatz ist APCA, der Accessible Perceptual Contrast Algorithm, der die Kandidaten-Kontrastmethode für WCAG 3 ist (noch ein Arbeitsentwurf, Jahre davon entfernt, ein Standard zu sein). Er ist in Weisen unterschiedlich aufgebaut, die wichtig sind:
- Er ist wahrnehmungsbasiert und basiert auf Helligkeitsunterschied, abgestimmt auf tatsächliche Lesbarkeitsstudien statt auf ein Luminanzverhältnis der 1990er.
- Er ist asymmetrisch. Polarität ist wichtig, sodass Dunkel-auf-Hell und Hell-auf-Dunkel auf ihre eigenen Bedingungen bewertet werden. Das behebt direkt die Dunkelmodusunterschätzung.
- Er berücksichtigt Schriftgröße und -gewicht als Teil der Punktzahl, anstatt der stumpfen zweiteiligen “normal vs. groß”-Aufteilung. Dünner 14px-Text benötigt mehr Kontrast als fetter 24px, und APCA sagt das.
- Seine Ausgabe ist ein Lc (Helligkeitskontrast)-Wert von ungefähr 0-106, kein Verhältnis. Eine gängige grobe Abbildung: Lc 60 für den Haupttext, Lc 75 für kleineren oder dünnen Text.
Ich ship noch nicht an APCA als Compliance-Ziel, weil WCAG 2.2 AA das ist, worauf Audits, Verträge und Klagen verweisen. Aber ich verwende eine APCA-Ablesung als Tiebreaker. Wenn ein Farbpaar kaum 4,5:1 abkratzt und APCA sagt, es ist schwach, vertraue ich APCA und passe an.
Ein Arbeitsablauf, der kein Raten beinhaltet
Hier ist die Schleife, die ich tatsächlich verwende, wenn ich eine Palette aufbaue.
- Wählen Sie zuerst die Farben für die Absicht - Marke, Hierarchie, Zustand - ohne sich um Kontrast zu kümmern. Verwenden Sie ein Tool wie den Farbwähler , um genaue Werte aus einem Mockup oder einem vorhandenen Bildschirm zu erfassen, anstatt aus dem Gedächtnis zu nähern.
- Bauen Sie den gesamten Satz als System auf, nicht paarweise. Der Farbpaletten-Generator ist nützlich, um eine Rampe aus Tönen und Schatten anzulegen, damit Sie legale Optionen bei jedem Schritt haben, bevor Sie anfangen, Text zu platzieren.
- Prüfen Sie jedes Text-auf-Fläche-Paar gegen die obige Tabelle. Die meisten Checker melden das Verhältnis direkt.
- Wenn ein Paar versagt, passen Sie die Helligkeit an, nicht den Farbton. Das ist die nützlichste Einzelregel. Die Markenidentität lebt hauptsächlich im Farbton und in der Sättigung; Kontrast lebt in der Helligkeit. Konvertieren Sie die Farbe in HSL mit dem Farbkonverter , dann senken oder erhöhen Sie den L-Wert, bis das Verhältnis besteht. Die Farbe liest sich immer noch als “Ihr Blau”, nur dunkler oder heller.
- Verifizieren Sie das Ergebnis so, wie Benutzer es sehen, einschließlich eines Farbenblindheits-Simulator -Durchgangs.
Hover-, Deaktiviert- und Fokus-Zustände
Der Ruhezustand ist der einfache Teil. Die Zustände sind der Ort, an dem Dinge gleiten.
- Hover: Lassen Sie einen Hover-Hintergrund Ihren Text nicht unter die Schwelle fallen. Wenn Ihr Button-Text im Ruhezustand besteht und der Hover-Zustand die Füllfarbe abdunkelt oder aufhellt, prüfen Sie den Text erneut bei der Hover-Farbe. Das sind verschiedene Paare.
- Deaktiviert: Deaktivierte Steuerelemente sind ausdrücklich von den Kontrastregeln ausgenommen, sodass Sie sie unter 4,5:1 ausgrauen können. Aber ausgenommen bedeutet nicht “unsichtbar machen”. Halten Sie den deaktivierten Zustand gut genug erkennbar, dass Benutzer wissen, dass das Steuerelement existiert; lassen Sie ihn aber nicht das einzige Signal für den deaktivierten Zustand sein, kombinieren Sie ihn mit einem Cursor- oder Label-Hinweis.
- Fokus: Geben Sie dem Fokus-Indikator sein eigenes 3:1 gegen die Seite, und stellen Sie sicher, dass er nicht die gleiche Farbe wie ein benachbartes aktives Element hat. Ein zweifarbiger Umriss (ein heller innerer Ring plus ein dunkler äußerer Ring) ist ein billiger Trick, der 3:1 gegenüber sowohl hellen als auch dunklen Umgebungen besteht.
/* Fokus-Ring, der 3:1 auf den meisten Hintergründen hält */
:focus-visible {
outline: 2px solid #1a1a1a;
outline-offset: 2px;
box-shadow: 0 0 0 4px #ffffff; /* heller Hof trennt den Ring vom Element */
}
Der Farbenblindheits-Aspekt
Kontrastverhältnis und Farbenblindheit sind verwandte, aber nicht dieselbe Problematik. Die Verhältnisformel nähert das für Sie bereits an - weil sie auf Luminanz basiert, ist ein Paar, das 4,5:1 besteht, normalerweise für alle Farbsehtypen unterscheidbar. Luminanz ist der Kanal, den die meisten Farbsehdefizite erhalten.
Die Falle ist, Farbe als den einzigen Träger von Informationen zu verwenden. Roter Fehlertext und grüner Erfolgstext können beide den Kontrast gegen Weiß bestehen und für jemanden mit Deuteranopie ununterscheidbar voneinander sein. Die Lösung ist nicht mehr Kontrast, es ist redundante Kodierung: ein Icon, ein Label, eine Form oder ein Positionsunterschied neben der Farbe. Führen Sie Ihre Fehler- und Statuszustände durch einen Simulator und bestätigen Sie, dass sie immer noch verschiedene Geschichten erzählen, wenn der Farbton herausgenommen wird.
Ungefähr 8% der Männer haben eine Form von Rot-Grün-Farbsehdefizit. Das ist kein Randfall, den man verschieben kann.
Zusammenfassung
Kontrast ist ein Luminanzvergleich, kein Farbvergleich, und die Ziele sind festgelegt: 4,5:1 für normalen Text, 3:1 für großen Text und alle bedeutungsvollen Nicht-Text-Elemente unter SC 1.4.11, 7:1 wenn Sie AAA anstreben. Großer Text bedeutet 24px Regular oder 18,66px Fett, und diese Ein-Pixel-Grenze ist real. Die WCAG 2.x-Formel linearisiert sRGB, gewichtet die Kanäle in Richtung Grün und fügt einen 0.05-Glanzoffset hinzu - und diese alternde Mathematik überschätzt den Dunkelmoduskontrast und stolpert über einige Mitteltöne, was genau das ist, was APCA und der WCAG 3-Entwurf zu beheben gebaut sind. Bis dahin bauen Sie die Palette als System auf, beheben Sie Versager durch Änderung der Helligkeit statt des Farbtons, geben Sie Fokus-Ringen ihr eigenes Kontrastbudget und lassen Sie niemals Farbe das einzige sein, was Bedeutung trägt.
In diesem Artikel erwähnte Tools
- Color Picker - Pick colors visually and get HEX, RGB, and HSL values.
- Color Converter - Convert colors between HEX, RGB, HSL and CMYK formats.
- Color Blindness Simulator - Simulate how colors appear with protanopia, deuteranopia, tritanopia, and achromatopsia using color transformation matrices.
- Color Palette Generator - Generate harmonious color palettes, Tailwind-style 50-900 scales and design-system tokens from any base hex color. Also a hex color palette generator (Farbpalette Generator auf Deutsch).