Color Contrast Checker
Controleer WCAG AA- en AAA-contrastverhouding tussen tekst- en achtergrondkleur
Voorgrondkleur (tekst)
Achtergrondkleur
Voorbeeld grote tekst (18px vet)
Voorbeeld normale tekst — zo ziet je bodytekst eruit tegen de gekozen achtergrondkleur.
Contrastverhouding
14.63:1
WCAG-conformiteit
Geslaagd
Normaal AA
Geslaagd
Normaal AAA
Geslaagd
Groot AA
Geslaagd
Groot AAA
WCAG AA — Normale tekst: 4.5:1, Grote tekst (18px+ of 14px vet): 3:1
WCAG AAA — Normale tekst: 7:1, Grote tekst (18px+ of 14px vet): 4.5:1
Wat is kleurcontrastcontrole?
Kleurcontrastcontrole meet het luminantieverschil tussen een voorgrondkleur (doorgaans tekst) en een achtergrondkleur, en drukt dit uit als een verhouding. Een verhouding van 1:1 betekent dat de kleuren identiek zijn; 21:1 is het maximum, wat zwart op wit (of omgekeerd) vertegenwoordigt. De Web Content Accessibility Guidelines (WCAG), gepubliceerd door het W3C, definiëren minimale contrastwaarden waaraan tekst moet voldoen, zodat mensen met slechtziendheid of kleurblindheid de tekst kunnen lezen.
De formule voor de contrastverhouding is afkomstig uit WCAG 2.x en maakt gebruik van relatieve luminantie: een maatstaf voor hoe helder een kleur aan het menselijk oog verschijnt. Relatieve luminantie wordt berekend door elk sRGB-kanaal te lineariseren (gamma verwijderen) en de kanalen te wegen met de ITU-R BT.709-coëfficiënten: 0,2126 voor rood, 0,7152 voor groen en 0,0722 voor blauw. Groen draagt het meeste bij omdat menselijke ogen het meest gevoelig zijn voor groen licht. De verhouding is vervolgens (L1 + 0,05) / (L2 + 0,05), waarbij L1 de luminantie van de lichtste kleur is.
WCAG definieert twee conformiteitsniveaus. Niveau AA vereist een contrastverhouding van minimaal 4,5:1 voor normale tekst en 3:1 voor grote tekst (18px of groter, of 14px vet). Niveau AAA verhoogt de lat naar respectievelijk 7:1 en 4,5:1. WCAG 2.1 introduceerde ook Succescriterium 1.4.11, dat een verhouding van 3:1 vereist voor niet-tekstuele UI-componenten zoals randen, iconen en focusindicatoren.
Waarom deze contrastchecker gebruiken?
Contrast beoordelen op het oog is onbetrouwbaar. Kleuren die op je gekalibreerde scherm duidelijk van elkaar te onderscheiden zijn, kunnen samenvloeien op een goedkoop laptopscherm, in direct zonlicht of voor iemand met deuteranopie. Een numerieke verhouding elimineert giswerk en geeft een duidelijk geslaagd/gezakt-oordeel op basis van de WCAG-norm.
Gebruiksscenario's voor de contrastchecker
WCAG-contrastvereisten
De onderstaande tabel geeft een overzicht van de minimale contrastwaarden die WCAG 2.1 vereist voor verschillende inhoudstypen en conformiteitsniveaus. Grote tekst is gedefinieerd als 18px (24 CSS-pixels) of groter bij normaal gewicht, of 14px (18,66 CSS-pixels) of groter bij vet gewicht.
| Niveau | Min. verhouding | Van toepassing op | Opmerking |
|---|---|---|---|
| AA Normal text | 4.5 : 1 | Body text, paragraphs, labels | Minimum for most UI text |
| AA Large text | 3.0 : 1 | Text >= 18px, or >= 14px bold | Minimum for headings |
| AA UI components | 3.0 : 1 | Borders, icons, focus indicators | Non-text contrast (WCAG 1.4.11) |
| AAA Normal text | 7.0 : 1 | Body text at highest standard | Enhanced readability |
| AAA Large text | 4.5 : 1 | Large text at highest standard | Enhanced for headings |
Luminantie, AA en AAA uitgelegd
De berekening van de contrastverhouding kent drie stappen: luminantie berekenen voor elke kleur, de verhouding afleiden en vergelijken met WCAG-drempelwaarden.
Codevoorbeelden
Bereken WCAG-contrastverhoudingen programmatisch. Elk voorbeeld implementeert de relatieve luminantieformule uit WCAG 2.x en de contrastverhoudingenberekening. Dezelfde zwart-op-wit en indigo-op-wit paren worden getest ter vergelijking.
// Calculate relative luminance per WCAG 2.x (sRGB)
function luminance(r, g, b) {
const [rs, gs, bs] = [r, g, b].map(c => {
c /= 255
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)
})
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs
}
// Contrast ratio between two RGB colors
function contrastRatio(fg, bg) {
const l1 = luminance(...fg)
const l2 = luminance(...bg)
const lighter = Math.max(l1, l2)
const darker = Math.min(l1, l2)
return (lighter + 0.05) / (darker + 0.05)
}
contrastRatio([0, 0, 0], [255, 255, 255]) // -> 21.0
contrastRatio([99, 102, 241], [255, 255, 255]) // -> 3.95
contrastRatio([29, 78, 216], [255, 255, 255]) // -> 6.06 (AA pass)def luminance(r: int, g: int, b: int) -> float:
"""Relative luminance per WCAG 2.x, ITU-R BT.709 coefficients."""
channels = []
for c in (r, g, b):
c /= 255
channels.append(c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4)
return 0.2126 * channels[0] + 0.7152 * channels[1] + 0.0722 * channels[2]
def contrast_ratio(fg: tuple, bg: tuple) -> float:
l1 = luminance(*fg)
l2 = luminance(*bg)
lighter, darker = max(l1, l2), min(l1, l2)
return (lighter + 0.05) / (darker + 0.05)
print(f"{contrast_ratio((0, 0, 0), (255, 255, 255)):.2f}") # -> 21.00
print(f"{contrast_ratio((99, 102, 241), (255, 255, 255)):.2f}") # -> 3.95
# Check WCAG AA for normal text
ratio = contrast_ratio((29, 78, 216), (255, 255, 255))
print(f"{ratio:.2f} — {'AA pass' if ratio >= 4.5 else 'AA fail'}") # -> 6.06 — AA passpackage main
import (
"fmt"
"math"
)
func linearize(c float64) float64 {
c /= 255
if c <= 0.04045 {
return c / 12.92
}
return math.Pow((c+0.055)/1.055, 2.4)
}
func luminance(r, g, b int) float64 {
return 0.2126*linearize(float64(r)) +
0.7152*linearize(float64(g)) +
0.0722*linearize(float64(b))
}
func contrastRatio(fgR, fgG, fgB, bgR, bgG, bgB int) float64 {
l1 := luminance(fgR, fgG, fgB)
l2 := luminance(bgR, bgG, bgB)
lighter := math.Max(l1, l2)
darker := math.Min(l1, l2)
return (lighter + 0.05) / (darker + 0.05)
}
func main() {
ratio := contrastRatio(0, 0, 0, 255, 255, 255)
fmt.Printf("%.2f\n", ratio) // -> 21.00
ratio = contrastRatio(29, 78, 216, 255, 255, 255)
fmt.Printf("%.2f\n", ratio) // -> 6.06 (AA pass for normal text)
}/* WCAG-safe color pairs — tested contrast ratios */
/* 12.63:1 — passes AAA normal text */
.high-contrast {
color: #1e293b; /* slate-800 */
background: #f8fafc; /* slate-50 */
}
/* 7.07:1 — passes AAA normal text */
.dark-theme-text {
color: #e2e8f0; /* slate-200 */
background: #0f172a; /* slate-900 */
}
/* 4.57:1 — passes AA normal, fails AAA */
.accent-on-white {
color: #1d4ed8; /* blue-700 */
background: #ffffff;
}
/* 2.14:1 — fails AA for text, but passes 3:1 for large text */
.muted-heading {
color: #94a3b8; /* slate-400 */
background: #ffffff;
}