Layer 8Ehrlicher Tech-Support

Grauer Text auf hellgrauem Grund: Kontrast wird gerechnet

Blasser Text ist keine Geschmacksfrage, sondern rechenbar. Die WCAG-Formel, die Schwellen 4,5:1 und 3:1 und die Falle namens Deckkraft.

Aufwand:
ca. 30 Min.
Voraussetzung:
Die Farbwerte aus dem eigenen Stylesheet, Ein Rechner oder ein Kontrastwerkzeug, Die Entwicklerwerkzeuge des Browsers
Gilt für:
Webseiten und Web-Anwendungen, Apps mit eigenen Farbwerten, WCAG 2.2 in der Fassung vom 12.12.2024

Das Symptom

Auf dem Bildschirm steht ein Hinweis, und er ist da — irgendwie. Grauer Text auf hellgrauem Kasten, „damit es ruhiger wirkt“. Wer ihn geschrieben hat, liest ihn mühelos. Der Vater am Küchentisch liest ihn nicht. Die Kollegin am Fenster liest ihn nicht. Auf dem Handy in der Sonne liest ihn niemand. Und dann kommt der Satz, der jede Diskussion darüber im Kreis laufen lässt: „Ich sehe das doch gut.“

Die „Layer 8“-Wahrheit

Kontrast ist der einzige Punkt der Barrierefreiheit, bei dem Geschmack gar keine Rolle spielt, weil es eine Formel gibt. Die WCAG definieren das Kontrastverhältnis als (L1 + 0,05) geteilt durch (L2 + 0,05), wobei L1 die relative Leuchtdichte der helleren und L2 die der dunkleren Farbe ist. Das Ergebnis liegt laut Anmerkung zwischen 1 und 21 — 1 bedeutet identisch, 21 ist Schwarz auf Weiß. Zwei Farbwerte hinein, eine Zahl heraus. Es gibt hier nichts zu finden, nur etwas auszurechnen.

Interessant ist, was in dieser Formel nicht vorkommt: der Farbton. Das W3C begründet das ausdrücklich — für Menschen mit Farbsinnstörung, die bestimmte Farbnuancen nicht unterscheiden können, haben Farbton und Sättigung nach der Lesegeschwindigkeit gemessen minimalen oder gar keinen Einfluss auf die Lesbarkeit. Deshalb wird Kontrast so berechnet, dass Farbe kein maßgeblicher Faktor ist. Wer also ein zartes Blau auf zartem Grün verteidigt, weil „die Farben sich doch deutlich unterscheiden“, verteidigt eine Eigenschaft, die für das Problem irrelevant ist.

Die Schwellen sind zwei. Fließtext braucht 4,5:1. Große Schrift darf auf 3:1 herunter, weil breitere Striche bei weniger Kontrast noch lesbar bleiben — aber „groß“ ist definiert und nicht verhandelbar: mindestens 18 Punkt, oder 14 Punkt in Fettschrift. Umgerechnet sind das rund 24 beziehungsweise 18,5 CSS-Pixel; das Verständnisdokument rechnet ausdrücklich mit 1pt = 1,333px. Und gerundet wird nicht: 4,499:1 erfüllt die Schwelle 4,5:1 nicht, und bei den Bedienelementen erfüllt 2,999:1 die 3:1 nicht. Diese Härte ist kein Selbstzweck. Eine Grenze, über die man verhandeln kann, ist die Grenze desjenigen, der am lautesten verhandelt.

Denn Text ist nur die Hälfte. Erfolgskriterium 1.4.11 verlangt dieselben 3:1 gegen die angrenzenden Farben für alles, was man braucht, um ein Bedienelement und seinen Zustand überhaupt als solches zu erkennen — und für die Teile von Grafiken, die zum Verständnis nötig sind. Das ist die Regel, an der die meisten modernen Entwürfe scheitern: Feldrahmen in einem Grau, das man erahnt, ein Häkchen im Ankreuzfeld, das im Kästchen fast verschwindet, eine Fokusanzeige, die auf dem hellen Hintergrund nicht mehr auffällt. Ein Formularfeld, dessen Rand man nicht sieht, ist für den Benutzer kein Feld. Es ist eine Fläche, in die man vielleicht klicken kann.

Und dann ist da die Falle, die diese Seite selbst gestellt und in sich getappt ist: die Deckkraft. Eine Textfarbe kann sauber gerechnet 5,90:1 auf dem Hintergrund erreichen und trotzdem an 44 Stellen unlesbar sein, weil dort nicht die Farbe steht, sondern die Farbe mit siebzig Prozent Deckkraft. Der Browser blendet sie dann gegen den Hintergrund: Aus 5,90:1 werden 3,44:1, bei sechzig Prozent 2,83:1, bei fünfzig Prozent 2,35:1. Im Editor sieht das nach einer Nuance aus. In der Rechnung ist es die Hälfte des Kontrasts. Betroffen war hier kein Zierrat, sondern Datumszeilen, Beschriftungen und beide Sucheingaben.

Die Lehre daraus steht seither als Skript im Verzeichnis und nicht als guter Vorsatz im Kopf. scripts/kontrast.mjs liest die Farbdefinitionen aus dem Stylesheet, damit sie nicht doppelt gepflegt werden, sammelt jede im Quelltext tatsächlich vorkommende Kombination aus Farbe und Deckkraft ein, blendet sie genau so, wie der Browser es tut, prüft sie gegen den ungünstigsten Hintergrund, auf dem sie stehen kann, und bricht ab, sobald eine einzige Stelle unter 4,5:1 fällt. Ausgenommen ist allein, was aria-hidden trägt — und das ist keine Hintertür, sondern die Regel selbst: 1.4.3 nimmt reine Dekoration und inaktive Bedienelemente ausdrücklich aus. Was ein Vorleseprogramm nicht ausspricht und was keine Information trägt, muss auch niemand lesen können.

In Apps gilt derselbe Maßstab mit anderen Einheiten. Androids eigene Anleitung empfiehlt 4,5:1 für Text unter 18sp — beziehungsweise unter 14sp bei Fettschrift — und 3:1 für allen übrigen Text. Es ist dieselbe Rechnung mit denselben Zahlen, nur dass die Schriftgröße dort in einer Einheit steht, die die Systemeinstellung des Benutzers mitskaliert.

Das eigentlich Ärgerliche an diesem Fehler: Er ist der billigste von allen. Kein Umbau, keine neue Bibliothek, keine Schulung — zwei Hexadezimalwerte in eine Formel, und in fast allen Fällen reicht es, eine Farbe zwei Stufen dunkler zu setzen. Was ihn trotzdem so verbreitet macht, ist der eine Satz, gegen den keine Formel hilft, solange niemand sie ausrechnet: „Ich sehe das doch gut.“

Die echte, verifizierte Lösung

1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:

  1. 1.

    Die tatsächlichen Farbwerte holen, nicht die vermuteten

    Gerechnet wird mit den Werten aus dem Stylesheet, nicht mit dem, was auf dem Bildschirm ankommt. Das Verständnisdokument sagt warum: Weil Autoren die Kantenglättung des Systems nicht steuern, sind die Vorder- und Hintergrundfarben aus Browser, Auszeichnung oder Stylesheet maßgeblich, nicht der gerenderte Text. Also: Textfarbe und die Farbe der Fläche darunter heraussuchen — beide, sonst gibt es nichts zu rechnen.

  2. 2.

    Das Verhältnis ausrechnen, statt es zu schätzen

    Die Formel steht im WCAG-Glossar und ist kurz: (L1 + 0.05) / (L2 + 0.05), mit L1 als relativer Leuchtdichte der helleren und L2 der der dunkleren Farbe. Das Ergebnis liegt zwischen 1 und 21. Jedes Kontrastwerkzeug rechnet dasselbe; wer es selbst tut, kommt mit zwanzig Zeilen aus:

    const leuchtdichte = ([r, g, b]) => {
      const [R, G, B] = [r, g, b].map((v) => {
        v /= 255;
        // WCAG 2.2: Schwelle 0.04045 (vor Mai 2021: 0.03928, laut W3C ohne praktische Auswirkung)
        return v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
      });
      return 0.2126 * R + 0.7152 * G + 0.0722 * B;
    };
    
    const verhaeltnis = (a, b) => {
      const [hell, dunkel] = [leuchtdichte(a), leuchtdichte(b)].sort((x, y) => y - x);
      return (hell + 0.05) / (dunkel + 0.05);
    };
    
  3. 3.

    Die richtige Schwelle wählen und nicht runden

    Fließtext braucht 4,5:1. Große Schrift — mindestens 18 Punkt, oder 14 Punkt fett — darf auf 3:1 herunter. In CSS-Pixeln entspricht das nach dem Verständnisdokument etwa 24px beziehungsweise 18,5px. Und der Wert wird nicht aufgerundet: 4,499:1 erfüllt die Schwelle 4,5:1 nicht, 2,999:1 nicht die 3:1. Wer sich zwei Hundertstel unterhalb der Grenze einrichtet, hat nichts gewonnen und die Diskussion nur vertagt.

  4. 4.

    Bedienelemente und Grafiken getrennt prüfen

    Erfolgskriterium 1.4.11 verlangt 3:1 gegen die angrenzenden Farben — für alles, was nötig ist, um ein Bedienelement und seinen Zustand zu erkennen, und für die Teile von Grafiken, die zum Verständnis nötig sind. Praktisch heißt das: Feldrahmen, Häkchen, Umschalter, Fokusanzeige, Diagrammlinien. Ein Eingabefeld, dessen Rahmen man nicht sieht, ist kein Eingabefeld, sondern eine leere Fläche mit Geheimnis.

  5. 5.

    Deckkraft als das behandeln, was sie ist: eine Kontrastbremse

    Der teuerste Fehler in diesem Projekt lief genau so. Eine Textfarbe schafft auf dem Seitenhintergrund saubere 5,90:1. Geschrieben wurde aber an 44 Stellen eine Deckkraft-Variante, und die Farbe wird dann gegen den Hintergrund geblendet: Aus 5,90:1 wurden bei 70 Prozent 3,44:1, bei 60 Prozent 2,83:1, bei 50 Prozent 2,35:1. Betroffen war kein Zierrat, sondern Datumszeilen, Beschriftungen und beide Sucheingaben. Wer eine blassere Stufe braucht, definiert sie als eigene Farbe und rechnet sie nach.

  6. 6.

    Immer beide Farben angeben

    Das Glossar der WCAG nennt einen Fall, der oft übersehen wird: Es ist ein Fehler, keine Hintergrundfarbe anzugeben, wenn die Textfarbe angegeben ist — weil die Voreinstellung des Benutzers unbekannt ist und der Kontrast damit nicht bewertet werden kann. Umgekehrt gilt dasselbe. Halbe Angaben sind nicht sparsam, sie sind unprüfbar:

    /* unprüfbar: die Fläche darunter ist unbekannt */
    .hinweis { color: #767676; }
    
    /* prüfbar: beide Seiten der Rechnung stehen da */
    .hinweis { color: #595959; background-color: #ffffff; }
    
  7. 7.

    Die Rechnung in die Auslieferung einbauen

    Eine Prüfung, die jemand von Hand macht, ist eine Erinnerung; eine, die den Build abbricht, ist eine Regel. Dieses Projekt hat dafür scripts/kontrast.mjs: Das Skript liest die Farb-Token aus src/styles/global.css, sammelt jede im Quelltext wirklich vorkommende Kombination aus Textfarbe und Deckkraft ein, blendet sie so, wie der Browser es tut, rechnet sie gegen den ungünstigsten möglichen Hintergrund und beendet sich mit Fehlercode, sobald eine Stelle unter 4,5:1 liegt. Ausgenommen ist nur, was aria-hidden trägt — also das, was ohnehin niemand vorgelesen bekommt:

    node scripts/kontrast.mjs
    

Häufige Fragen (FAQ)

?Woher kommt die Zahl 4,5:1, und was misst sie überhaupt?

Sie misst Helligkeit, nicht Farbe. Die WCAG definieren das Kontrastverhältnis als (L1 + 0.05) / (L2 + 0.05), wobei L1 die relative Leuchtdichte der helleren und L2 die der dunkleren Farbe ist. Die möglichen Werte reichen laut Anmerkung von 1 bis 21, also von „identisch“ bis „Schwarz auf Weiß“. Warum Helligkeit und nicht Farbton, steht im Verständnisdokument: „For people with color vision deficiency who are not able to distinguish certain shades of color, hue and saturation have minimal or no effect on legibility as assessed by reading performance“ — deshalb sei Kontrast „calculated in such a way that color (hue) is not a key factor“. Erfolgskriterium 1.4.3 verlangt darauf aufbauend für Text und Textbilder mindestens 4.5:1.

?Mein Text ist groß. Gilt dann eine andere Grenze?

Ja, und zwar 3:1 — aber „groß“ ist definiert, nicht gefühlt. Das WCAG-Glossar sagt zu großer Schrift: „with at least 18 point or 14 point bold or font size that would yield equivalent size for Chinese, Japanese and Korean (CJK) fonts“. Das Verständnisdokument rechnet das um: „The ratio between sizes in points and CSS pixels is 1pt = 1.333px, therefore 14pt and 18pt are equivalent to approximately 18.5px and 24px.“ Und es warnt vor dünnen Schnitten: Schriften mit außergewöhnlich dünnen Strichen sind schwerer zu lesen, besonders bei niedrigem Kontrast. Eine 24 Pixel große Haarlinien-Schrift erfüllt die Regel und scheitert trotzdem am Menschen.

?Bei mir kommen 4,49:1 heraus. Ist das nicht praktisch dasselbe?

Für das Auge vielleicht, für das Kriterium nicht. Das Verständnisdokument zu 1.4.3 stellt ausdrücklich klar: „The 3:1 and 4.5:1 contrast ratios referenced in this success criterion are intended to be treated as threshold values. When comparing the computed contrast ratio to the Success Criterion ratio, the computed values should not be rounded (e.g., 4.499:1 would not meet the 4.5:1 threshold).“ Bei 1.4.11 steht dieselbe Regel mit dem Beispiel 2.999:1 gegen 3:1. Der Sinn dahinter ist nicht Pedanterie, sondern Nachprüfbarkeit: Eine Schwelle, die auf Zuruf gerundet werden darf, ist keine Schwelle.

?Gilt das nur für Buchstaben?

Nein. Erfolgskriterium 1.4.11 verlangt mindestens 3:1 gegen die angrenzenden Farben für zwei Dinge: die visuelle Information, die nötig ist, um Bedienelemente und ihre Zustände zu erkennen, und die Teile von Grafiken, die zum Verständnis des Inhalts nötig sind. Das trifft genau die Stellen, an denen im Entwurf gern gespart wird — hellgraue Feldrahmen, das Häkchen im Ankreuzfeld, der Pfeil an einem geöffneten Menü, die Linien in einem Diagramm. Ausgenommen sind ausdrücklich inaktive Bauteile und solche, deren Aussehen der Browser bestimmt und der Autor nicht verändert hat.

?Warum fällt so etwas nicht beim Anschauen auf?

Weil derjenige, der es baut, es lesen kann. Auf einem guten Monitor, bei guter Beleuchtung, mit der Erwartung, was dort steht. Genau deshalb ist der Kontrast das einzige Barrierefreiheitsthema, das man vollständig einer Maschine übergeben kann: Es gibt eine Formel, es gibt Schwellenwerte, und beide Eingaben stehen im eigenen Stylesheet. Dieses Projekt hat dafür ein eigenes Prüfskript, weil das Schauen hier nachweislich versagt hat — 44 Stellen sahen im Editor nach einer Nuance aus und lagen in Wahrheit unter der Schwelle.

Belege

Diese Seite ist gegen die folgenden Primärquellen geprüft – Hersteller, Standard oder Gesetzestext. Wer es nicht glaubt, muss es nicht: Da steht, wo es nachzulesen ist.

Zuletzt geprüft am . Dokumentation altert – ein Datum daneben ist der einzige ehrliche Umgang damit.

Auch zu finden unter

Verwandte Fehler