Auditing text colours on a site or app
Put the text and background colours from the design file into the lesson 8 pickers and you immediately know whether they clear AA; placeholder text, disabled states and helper text are the most common failures.
Guide
Colour and Contrast is a 17-lesson interactive course that teaches you to read every colour notation that turns up in a design file or in CSS, and to judge with a measurable standard whether “these two colours can be read together”. Each lesson gives you one to three colour pickers and a concrete goal — push lightness to a value, get contrast past 4.5:1, find three hues 120° apart, make a pair of colours distinguishable for someone with colour vision deficiency. You drag a slider or type a colour value, and the numeric readings, the contrast badge, the colour wheel and the CVD simulation table update live while the checker tells you what is still off.
Updated 2026-09-094 sources14 min read
Colour and Contrast is a 17-lesson interactive course that teaches you to read every colour notation that turns up in a design file or in CSS, and to judge with a measurable standard whether “these two colours can be read together”. Each lesson gives you one to three colour pickers and a concrete goal — push lightness to a value, get contrast past 4.5:1, find three hues 120° apart, make a pair of colours distinguishable for someone with colour vision deficiency. You drag a slider or type a colour value, and the numeric readings, the contrast badge, the colour wheel and the CVD simulation table update live while the checker tells you what is still off.
The course has four parts: colour models (what each component of RGB, HEX, HSL, HSV and OKLCH controls, and why HSL “lightness” is not the brightness your eye perceives); contrast and WCAG (the relative luminance formula, and what the 4.5:1, 3:1 and 7:1 thresholds each govern); colour schemes (the complementary, triadic and analogous relationships on the wheel); and accessibility in practice (what the world looks like with red-green colour blindness, how to choose a CVD-safe palette, and how to count the main colours in a logo). The last lesson asks you to pull everything together and submit a three-colour set that passes every check.
It takes about 45 minutes. It is for front-end developers, designers and anyone who has to pick colours for slides, charts or posters; no art background is needed.
#RRGGBB, rgb() or hsl().?lesson=n.The course itself is above this page: pick a lesson in the contents on the left, drag sliders or type a colour value in the middle, and the verdict appears as you go.
Lesson 8, “WCAG AA: body text needs at least 4.5:1”, fixes the background as white and asks you to get the text colour past 4.5:1 without using pure black. Here is one attempt:
Background (fixed) #FFFFFF relative luminance L = 1.000
Attempt 1 #3B82F6 L = 0.228 contrast = 1.05 / 0.278 = 3.68:1
verdict ✓ AA large text (≥ 3) ✕ AA body text (≥ 4.5)
Attempt 2 #2563EB L = 0.153 contrast = 1.05 / 0.203 = 5.17:1
verdict ✓ AA large text ✓ AA body text ✕ AAA body text (≥ 7) → passed
Lesson 15, “CVD-safe”, takes the orange #E76F51 as its reference and starts the second colour at the green #8AB17D: for normal vision ΔE ≈ 0.20 and they are obviously different, but under deuteranopia simulation it falls to 0.056 and they almost coincide. Change the second colour to a medium blue such as #2563EB and the simulated ΔE returns above 0.15, while both colours stay ≥ 3:1 against white — that passes.
Every pixel on a screen is made of red, green and blue subpixels, and rgb(37 99 235) is the intensity of each of the three lights, 0 being off and 255 full brightness. All three at 0 is black, all three at 255 is white, and equal values give grey. HEX is the same set of numbers written in hexadecimal shorthand: in #2563EB, 25 = 37, 63 = 99 and EB = 235, two digits per channel; the three-digit form #FFF repeats each digit. This notation is called sRGB, the default colour space of the web and of the great majority of displays; there is also a gamma curve between the numbers and the physical light intensity, which matters when contrast is computed later.
RGB is convenient for machines and unintuitive for people: to make a blue “lighter” you have to change three channels at once. HSL uses different coordinates: hue H is an angle on the colour wheel (0° red, 120° green, 240° blue, back to red at 360°), saturation S is how pure the colour is (0% grey, 100% most vivid) and lightness L is how bright it is (0% black, 50% the pure colour, 100% white). Making it lighter means raising L, and a pastel means low S with high L — lesson 4 has you mix one by hand. In CSS you write hsl(221 83% 53%), which is the same colour as #2563EB.
In HSV (also called HSB) the V is “the largest of the three channels”: at V = 100% the colour is at its most vivid or brightest, and pulling S to 0 gives white rather than grey. In HSL, L = 100% is always white. Design software usually offers an HSV picker (a square plus a hue strip) while CSS supports only HSL; the two convert into each other losslessly, but the components mean different things, so HSV numbers should not be typed straight into hsl().
hsl(60 100% 50%) is pure yellow and hsl(240 100% 50%) is pure blue, both with L at 50%, yet the yellow looks clearly brighter. That is because HSL is only a geometric rearrangement of RGB values with no relation to human perception. OKLab / OKLCH (Ottosson, 2020) is fitted to data from human vision experiments so that equal L means equally bright to the eye, and changes in C (chroma) and h (hue) are more even as well. CSS Color 4 has adopted oklch() and mainstream browsers support it. Lesson 6 asks you to work from the OKLCH reading alone and bring a colour to a lightness L between 0.68 and 0.72 in the orange hue range — and you will find that the HSL L reading does not correspond to it. For gradients, for generating a light-to-dark ladder within one hue, and for keeping “equally bright” across hues, OKLCH is far more stable.
The contrast WCAG defines is not the difference between two arbitrary numbers but a ratio of relative luminance:
where , and the 0.05 models ambient light reflecting off the screen. Relative luminance comes from gamma-decoding the three sRGB channels and then weighting them:
Green carries the largest weight (0.7152), which is why a green of the same numeric value looks much brighter than a blue; black against white is the 21:1 ceiling. Lesson 7 has you work one out by hand against the readings, and from then on the page computes it for you.
WCAG 2.2's 1.4.3 (level AA) requires body text contrast of ≥ 4.5:1 and large text (about 24 px regular or 18.5 px bold) of ≥ 3:1; 1.4.11 requires “non-text content” such as icons, borders and charts to reach ≥ 3:1 as well; and 1.4.6 (level AAA) raises body text to ≥ 7:1. These are not aesthetic standards but empirical thresholds that keep text readable for users with moderate low vision, and they are the basis of accessibility law in many countries.
Lesson 9 deliberately asks for “past 7:1 but not pure black”: #000 on #FFF is 21:1 and perfectly fine, but large areas of pure black and white tend to glare, so designs usually choose deep greys such as #1A1A1A or #24292F, which still clear 7:1 comfortably. Conversely, the light grey placeholder text #9CA3AF is only 2.5:1 on white, which is where a lot of websites fall short.
The most basic part of colour theory is the angular relationship between hues:
The course's wheel marks these three positions for the current colour; the check only looks at the hue angle differences (allowing a small tolerance) while saturation and lightness are free — in real palettes they often matter more than hue.
About 8% of men and 0.5% of women have some colour vision deficiency, the most common being red-green (protanopia, missing red, and deuteranopia, missing green); blue-yellow (tritanopia) is rarer and total colour blindness rarer still. The course simulates it with the physiological model of Machado et al. from 2009: the colour is projected onto the cone responses of the affected viewer and converted back to sRGB. It also quantifies how far apart two colours are as the Euclidean distance ΔE in OKLab space: within about 0.02 they are practically indistinguishable, and at ≥ 0.15 they are obviously different.
Lesson 14 asks you to pick, from three pairs, the one a deuteranope would find hardest — the orange #E76F51 and green #8AB17D fall to a ΔE of 0.056 under simulation, while red against cyan and dark blue against yellow stay clear because their lightness differs so much. Lesson 15 asks you to fix it. There are two workable moves: separate the lightness (colour blindness mostly loses hue information, while a lightness difference always survives) or change the hue (red/green to blue/orange). Charts should also add shape, texture or the numbers themselves rather than carrying information in colour alone — which is what WCAG 1.4.1 asks for.
The naive way to “extract the main colours” from an image is to count pixels: quantise each pixel's RGB channels to 16 levels (4096 buckets), count, sort, and take the average colour of the top few buckets. The lesson draws a sample logo on a canvas, counts it and gives a palette ordered by share, which you can pick from by clicking a swatch or sampling the image directly. Real tools (Vibrant.js, Material You's dynamic colour) also cluster, discard near-white and near-black, and weight by saturation, but the principle is the same.
The last lesson combines everything: submit three colours where each reaches ≥ 4.5:1 against white (so any of them can be body text or button text), any two are ≥ 60° apart in hue (so they can be told apart), and their OKLCH lightness is close to equal (so they feel unified). A colour decision in a real project is roughly the intersection of these three constraints: readable, separable, unified.
#F00 and pure green #0F0 are 120° apart in hue yet only 2.9:1 in contrast, and someone with red-green colour blindness can barely tell them apart.Put the text and background colours from the design file into the lesson 8 pickers and you immediately know whether they clear AA; placeholder text, disabled states and helper text are the most common failures.
The CVD simulation table in lessons 14–15 checks directly whether a categorical palette stays distinguishable for someone with red-green deficiency; if not, change the hue or add a lightness difference, then add shape and labels.
Extract the main colour from the logo (the idea in lesson 16), find an accent with the wheel relationships from lessons 11–13, and choose the body colour with a contrast lesson — lesson 17 is a rehearsal of exactly that flow.
oklch(0.7 0.15 250) stops being unfamiliar: you know the three numbers are perceptual lightness, chroma and hue, and why it produces smoother gradients than HSL.
Contrast is decided by relative luminance, and saturated blues, purples and reds can look “deep” while still being fairly bright, and the ratio against white is also affected by the +0.05 term. Watch the contrast value in the numeric readings and lower the HSL L step by step until it crosses.
No. Every lesson is checked against a numeric range (a contrast threshold, a hue angle difference, a ΔE threshold), so any colour that satisfies the condition passes.
Figma and Sketch still mostly offer HSB pickers, but CSS accepts oklch() directly; the course's numeric readings panel gives all five notations at once, so you can copy the line you need.
Machado 2009 is one of the models used in academia and by mainstream tools (Chrome DevTools, Color Oracle), and its simulation of full deficiency is a reliable approximation; individual variation and partial deficiency cannot be covered by a single matrix.
No, progress lives only in the current browser's localStorage; “Share this lesson” copies a link so you can continue from the same lesson elsewhere, but the completion record does not travel with it.
The whole course runs locally in the browser: colour conversion, contrast, CVD simulation and pixel counting are pure in-page calculations with no server request; the sample logo is drawn on a canvas by the page itself and loads no external image; progress is stored in local localStorage.
hsl() and oklch(): https://www.w3.org/TR/css-color-4/(访问日期:2026-09-09)Updated 2026-09-09
17 lessons: what RGB/HEX/HSL/HSV/OKLCH mean, WCAG contrast, complementary/triadic/analogous palettes, color-blindness simulation and brand color extraction, judged live as you drag sliders
Goal拖动 R、G、B 三个滑杆,把颜色调成橙色 #FF8000(R=255,G=128,B=0)。
屏幕用红、绿、蓝三种光混色(加色法):三者全开是白,全关是黑。每个通道 0–255,所以一共 256³ ≈ 1677 万种颜色。#FF8000 的意思是红光全开、绿光一半、蓝光关闭,得到橙色。判定用的是感知距离 ΔE(OKLab),不必精确到每一个数字,肉眼分不出就算通过。