Color systems and contrast: a palette you can read
Why:You’ll build a palette that looks cohesive and can be read by people with low vision, and you’ll check contrast with numbers, not by eye. Afterwards you know the roles of colors, can build steps from one hue, and can make a light and a dark theme.
25 minutes
Read by speech synthesis (Qwen3-TTS); the voice is not a person’s.
A color system is a set of steps and roles (background, text, accent, status colors) that all colors are taken from. WCAG measures text contrast.
Color as a system
Colors on a site aren’t chosen “each time as the situation goes”. If a button is one blue on one page and another blue on the next, the visitor stops understanding what is a button. So colors are gathered into a system: a set of steps and roles from which every value is taken.
There are usually four roles.
- Surface: the background of the page, of cards and of panels.
- Text: body and secondary (captions, explanations).
- Accent: what you want people to press: the main button and links.
- Status colors: error, success, warning, hint. By default they are red, green, yellow and blue, and they must not coincide with the accent.
Names are taken by role, not by look: --accent, not --blue. If the accent turns green tomorrow, the rule about the main button isn’t affected.
The work goes in this order: first the neutral steps (from nearly white to nearly black, used to build backgrounds and text), then the accent steps, then the status colors, then the dark theme. In the end color takes up little room: on most good pages it is 90% neutral, a little accent and a scattering of status colors.
Check yourself: why are colors named by role and not by look?
So you can change a color without rewriting the rules: the role (“main action”) stays, and only the value changes.
Steps from one hue
Instead of one blue, you take a row: from very light to very dark, usually eleven steps (50, 100, 200 and on up to 900 and 950). Backgrounds are made from the light ones, text from the dark ones, the accent from the middle ones.
In HEX or RGB it is awkward to build a row: for different hues the same “number” looks differently light. In OKLCH lightness is perceived evenly, so a row of equal lightness steps gives an even ladder for any hue. It has three numbers: lightness (L from 0 to 1), chroma, or saturation (C, up to about 0.4) and hue (H in degrees). You met them in the lesson on typography and color.
Move the hue and the chroma. The steps are built automatically, and below, the contrast is calculated for every “text on background” pair.
| Pair | Colors | Contrast | Result |
|---|---|---|---|
| Body text (neutral 900 on 50) | #2c3136 / #f5f9fd | 12.42 to 1 | ✓ AAA |
| Secondary text (neutral 700 on 50) | #565b62 / #f5f9fd | 6.44 to 1 | ✓ AA |
| Link (accent 700 on 50) | #275b9a / #f5f9fd | 6.48 to 1 | ✓ AA |
| Button: white text on accent 600 | #ffffff / #3672bc | 4.88 to 1 | ✓ AA |
| Accent 300 on 50, too light | #a3cafd / #f5f9fd | 1.59 to 1 | ✕ fails |
Notice that the steps are chosen at an equal step of lightness, so the roles stay on the same numbers for any hue. And in the dark theme the same roles are played by other steps: it is not an inversion but a new arrangement. If a pair fails for some hue, move the step one place toward dark (in the light theme) or toward light (in the dark one).
Check yourself: why is OKLCH more convenient than HEX for steps?
Lightness in OKLCH is perceived evenly: an equal step gives an equal change to the eye for any hue.
Contrast is calculated, not judged by eye
Why you can’t judge contrast by eye, and what the luminance ratio of text and background is (from 1 to 1 up to 21 to 1, black on white), you know from the lesson on typography and color; the main mistake is there too: light gray text on white. Here are the same thresholds with the numbers of the WCAG 2.2 success criteria that auditors cite:
- 4.5 to 1 for ordinary text (criterion 1.4.3, Contrast (Minimum));
- 3 to 1 for large text (from 24 pixels, or from 18.66 pixels in bold);
- 3 to 1 for icons, field borders and other parts of an interface (criterion 1.4.11).
The formula goes like this: first the relative luminance is worked out for each color (allowing for the fact that a screen is not linear), then the lighter is divided by the darker, with 0.05 added to both. You don’t have to calculate by hand: there are services (the WebAIM Contrast Checker, in the sources), and the browser’s developer tools show the ratio right when you pick a color. The table in the diagram above uses the same formula, and you can check it yourself.
What is useful to remember:
- Text over a picture or a gradient. Check at the lightest spot under the letters, or, more reliably, put a solid semi-transparent plate behind.
- States. A button on hover and on focus must pass too.
- A new method of assessment, APCA, is in preparation (in the drafts of WCAG 3). For now, the official standard that laws and auditors cite is the luminance ratio from WCAG 2.
Check yourself: what contrast does ordinary text need?
At least 4.5 to 1; large text and icons need only 3 to 1.
Not by color alone
About one man in twelve, and far fewer women, can’t tell some colors apart, and there are people with other vision impairments, and just bright sun. So meaning must not be carried by color alone (criterion 1.4.1):
- an error needs a word or an icon (“Error: the phone number is not valid”), not only a red border;
- a link in text needs an underline, not only a different color;
- on a chart, different lines are told apart by the shape of their points or by a label, not only by hue.
Light and dark themes
A dark theme is not an inversion of the light one. You can’t just swap white for black, for three reasons. Pure black and pure white are harsher on the eye than dark gray and a warm light. Saturated colors on dark “vibrate”. Shadows can’t be seen on dark, so height is shown by being lighter than the background, not darker.
That is why in a dark theme the same roles are played by other steps: the background is step 950, the text step 100, the accent step 300 to 400. It is switched on by the person’s system setting.
@media (prefers-color-scheme: dark) {
:root {
--surface: #14130f;
--text: #f2efe9;
}
} And do tell the browser that the page can do both themes, with the meta tag <meta name="color-scheme" content="light dark">: then form fields and scroll bars are recolored too. Give body an explicit background: otherwise dark text may end up on dark.
We can’t check the @media rule automatically, so in the practice the dark theme is made as a [data-theme="dark"] block right on the page: this way you see both themes side by side.
Practice
Your project’s page has pale colors right now. Build a palette and recolor the page, checking the pairs against the contrast table. For now write all the colors straight into the rules: that way the contrast check reads them unambiguously. In later lessons you’ll replace them with variables, and the page will recolor completely with one change.
While you’re at it, make an error message that is clear without color, and a dark block with its own arrangement of steps.
What was wrong with 2014
In 2014 there were no dark themes in browsers, and contrast was rarely checked: flat design loved light gray on white. The WCAG contrast requirement has existed since 2008, but it was written into tenders and laws much later. Today it is checked automatically, and a page with gray text won’t pass.
Practice
Preparing your page…
Test
Question 1 of 6
Cards
These cards go into your review once you finish the lesson.
What contrast does text need?
Answer
At least 4.5 to 1 for ordinary text, 3 to 1 for large text (from 24 px, or 18.66 px bold) and for icons and field borders (WCAG 2.2).
What roles do colors have in a system?
Answer
Surface (background), text, accent (the main action, links) and status colors (error, success, warning). Colors are named by role, not by look.
Why OKLCH?
Answer
Lightness in it is perceived evenly, so steps of one lightness increment look equal to the eye for any hue.
How is a dark theme made?
Answer
Not by inversion: with other steps for the same roles. The background is dark gray, the text isn’t pure white, the accent is lighter. It is switched on with prefers-color-scheme.
Can meaning be carried by color alone?
Answer
No. An error gets a word or an icon, a link gets an underline: that way the meaning is available even to people who can’t tell colors apart.
Sources
- Understanding Contrast (Minimum), WCAG 2.2 (W3C) link only, in English
- Understanding Use of Color, WCAG 2.2 (W3C) link only, in English
- Color contrast (MDN) CC BY-SA, in English
- OKLCH in CSS: why we moved from RGB and HSL (Evil Martians) link only, in English
- prefers-color-scheme (MDN) CC BY-SA, in English
- Color roles (Material Design 3) link only, in English
Next
You now know: the color system, contrast in a color system.