OKLCH Studio · Accessible palettes · Data visualisation
Colorblind-friendly chart palette: six colours with a strict ΔE check
Updated 2026-10-19
If your audience may include people with colour-vision deficiency, which at roughly one in twelve men is not a niche, the safest chart is one that uses few colours and keeps them far apart. This palette trades quantity for separation: six colours, with a minimum pairwise difference well above the one used for the eight-colour set.
Every series colour is at least 3:1 against the chart background, and every pair is tested under normal vision and simulated protanopia, deuteranopia and tritanopia. The check is strict enough that the palette also tolerates blur, small markers and print on a mid-quality printer, all of which shrink differences further.
This page is the cautious option. Use it when charts are the product, as in analytics or public reporting, or when the people reading them cannot ask you what a colour means. Chart text is held at AAA here, as small axis labels are often the first thing to fail.
How to build a palette like this by hand is explained in the accessible colour palette guide; this page is the finished, verified result.
The palette: light and dark
Hex values are the sRGB result after gamut mapping. The oklch() values are those of the final hex, so converting either gives the same colour.
Light chart: series, with contrast on the chart background
- 1. #004B988.29:1
- 2. #B484003.26:1
- 3. #7730009.24:1
- 4. #9D6FE33.50:1
- 5. #4C9E843.12:1
- 6. #7D66225.37:1
Light theme
- Chart background
#F9FCFF
oklch(0.990 0.005 247.9)
Sample text
- Chart text
#1D2022
oklch(0.241 0.006 236.8)
Sample text
- Axis labels
#55585C
oklch(0.459 0.008 255.5)
Sample text
- Axis line
#83878B
oklch(0.621 0.008 247.9)
Sample text
- Gridline
#CCD0D5
oklch(0.856 0.008 253.9)
Sample text
- Series 1
#004B98
oklch(0.421 0.142 255.6)
Sample text
- Series 2
#B48400
oklch(0.6433 0.1323 82.79)
Sample text
- Series 3
#773000
oklch(0.403 0.112 46.6)
Sample text
- Series 4
#9D6FE3
oklch(0.640 0.171 299.9)
Sample text
- Series 5
#4C9E84
oklch(0.640 0.090 170.2)
Sample text
- Series 6
#7D6622
oklch(0.520 0.090 89.9)
Sample text
Dark chart: series, with contrast on the chart background
- 1. #86B8FF8.90:1
- 2. #CC59004.31:1
- 3. #5A76E44.44:1
- 4. #98B3057.58:1
- 5. #FFB5A810.73:1
- 6. #A969884.37:1
Dark theme
- Chart background
#141618
oklch(0.199 0.005 248.1)
Sample text
- Chart text
#E8EBEF
oklch(0.939 0.006 255.5)
Sample text
- Axis labels
#AEB1B6
oklch(0.760 0.008 260.7)
Sample text
- Axis line
#717579
oklch(0.560 0.008 248.0)
Sample text
- Gridline
#33373A
oklch(0.334 0.008 240.1)
Sample text
- Series 1
#86B8FF
oklch(0.775 0.115 257.2)
Sample text
- Series 2
#CC5900
oklch(0.600 0.166 47.2)
Sample text
- Series 3
#5A76E4
oklch(0.600 0.169 269.9)
Sample text
- Series 4
#98B305
oklch(0.7206 0.1701 119.98)
Sample text
- Series 5
#FFB5A8
oklch(0.839 0.089 30.2)
Sample text
- Series 6
#A96988
oklch(0.600 0.091 349.7)
Sample text
Contrast table: every verified pair
Pass or fail uses the raw WCAG 2.x ratio; the ratio shown is floored to two decimals so a near miss is never rounded up. APCA Lc is signed: positive is dark on light, negative is light on dark. Each row opens the pair in the checker.
| Pair | WCAG ratio | Target | Normal AA / AAA | Large AA / AAA | APCA Lc | APCA tier | Check |
|---|---|---|---|---|---|---|---|
| Chart text on background#1d2022 on #f9fcff | 15.91:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +101.3 | Lc 75: body text | Open in checker |
| Axis labels on background#55585c on #f9fcff | 6.94:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +82.8 | Lc 75: body text | Open in checker |
| Axis line on background#83878b on #f9fcff | 3.51:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | +61.8 | Lc 60: larger content text | Open in checker |
| Series 1 on background#004b98 on #f9fcff | 8.29:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | +86.6 | Lc 75: body text | Open in checker |
| Series 2 on background#b48400 on #f9fcff | 3.26:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | +58.8 | Lc 45: headlines and large text | Open in checker |
| Series 3 on background#773000 on #f9fcff | 9.24:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | +89.3 | Lc 75: body text | Open in checker |
| Series 4 on background#9d6fe3 on #f9fcff | 3.50:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | +61.4 | Lc 60: larger content text | Open in checker |
| Series 5 on background#4c9e84 on #f9fcff | 3.12:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | +57.1 | Lc 45: headlines and large text | Open in checker |
| Series 6 on background#7d6622 on #f9fcff | 5.37:1 | 3:1 (WCAG 1.4.11) | Pass / Fail | Pass / Pass | +75.5 | Lc 75: body text | Open in checker |
| Pair | WCAG ratio | Target | Normal AA / AAA | Large AA / AAA | APCA Lc | APCA tier | Check |
|---|---|---|---|---|---|---|---|
| Chart text on background#e8ebef on #141618 | 15.16:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −93.7 | Lc 75: body text | Open in checker |
| Axis labels on background#aeb1b6 on #141618 | 8.43:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −59.1 | Lc 45: headlines and large text | Open in checker |
| Axis line on background#717579 on #141618 | 3.90:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | −28.5 | Below Lc 30 | Open in checker |
| Series 1 on background#86b8ff on #141618 | 8.90:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | −61.9 | Lc 60: larger content text | Open in checker |
| Series 2 on background#cc5900 on #141618 | 4.31:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | −32.6 | Lc 30: non-text and incidental | Open in checker |
| Series 3 on background#5a76e4 on #141618 | 4.44:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | −33.1 | Lc 30: non-text and incidental | Open in checker |
| Series 4 on background#98b305 on #141618 | 7.58:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | −54.3 | Lc 45: headlines and large text | Open in checker |
| Series 5 on background#ffb5a8 on #141618 | 10.73:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | −72.0 | Lc 60: larger content text | Open in checker |
| Series 6 on background#a96988 on #141618 | 4.37:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | −32.4 | Lc 30: non-text and incidental | Open in checker |
Colour-blind check
Colours are simulated with the Machado et al. (2009) model at full severity (culori). The figure is the smallest ΔE-OK ×100, the Euclidean distance in OKLab, between any two colours. The build fails if it drops below 11. Still pair colour with labels or patterns (WCAG 1.4.1).
| Vision | Simulated colours | Min ΔE-OK ×100 | Closest pair |
|---|---|---|---|
| Normal vision | 13.1 (≥ 11) | 2 and 6 | |
| Protanopia | 11.4 (≥ 11) | 2 and 5 | |
| Deuteranopia | 13.0 (≥ 11) | 5 and 6 | |
| Tritanopia | 11.3 (≥ 11) | 4 and 6 |
| Vision | Simulated colours | Min ΔE-OK ×100 | Closest pair |
|---|---|---|---|
| Normal vision | 14.0 (≥ 11) | 2 and 6 | |
| Protanopia | 13.3 (≥ 11) | 1 and 5 | |
| Deuteranopia | 13.4 (≥ 11) | 2 and 6 | |
| Tritanopia | 12.8 (≥ 11) | 4 and 6 |
Why these colours
The selection method is the same as the larger categorical set: candidates that clear 3:1, then a search for the set with the largest smallest-pair distance over the four vision types. With only six colours, the optimiser has more room, and the accepted threshold is 11 instead of 8.
The resulting set mixes hue and lightness deliberately. Dichromats still see blue-yellow differences, and all viewers see lightness, so the set contrasts dark against light and blue against orange or brown, instead of relying on red-green. Achromatopsia, the rare case of no colour perception, is covered only by lightness and patterns, which is why the advice to add markers matters.
The generator solved 18 pairs (9 light, 9 dark), and all of them meet their target on the raw WCAG ratio. The tightest is series 5 on background in the light theme at 3.12:1 against a target of 3:1.
How to use it
- Pair every colour with a second encoding: direct labels, marker shapes or dash styles.
- Do not add a seventh colour; if you need more categories, group some or split into small multiples.
- Avoid placing the two closest series next to each other where lines cross; use the table below to see which pair is closest under each simulation.
- Show a legend in the same order as the data and test your chart with a colour-blindness simulator before shipping.
- Provide a data table or text summary so the chart is not the only route to the numbers.
Chart advice
- Prefer labelled lines and bars to legends the reader has to look up.
- Use a thin background-coloured outline between stacked or adjacent fills.
- Check the chart in grayscale as a quick extra test.
Export
Generated from the verified values above, never typed by hand. Light and dark are both included.
CSS custom properties (hex, then oklch())
/* sRGB hex first: every browser. */
:root {
--color-background: #f9fcff;
--color-text: #1d2022;
--color-text-muted: #55585c;
--color-axis: #83878b;
--color-gridline: #ccd0d5;
--color-series-1: #004b98;
--color-series-2: #b48400;
--color-series-3: #773000;
--color-series-4: #9d6fe3;
--color-series-5: #4c9e84;
--color-series-6: #7d6622;
}
.dark {
--color-background: #141618;
--color-text: #e8ebef;
--color-text-muted: #aeb1b6;
--color-axis: #717579;
--color-gridline: #33373a;
--color-series-1: #86b8ff;
--color-series-2: #cc5900;
--color-series-3: #5a76e4;
--color-series-4: #98b305;
--color-series-5: #ffb5a8;
--color-series-6: #a96988;
}
/* oklch() where supported: the same colours, unclipped values of the hex above. */
@supports (color: oklch(0 0 0)) {
:root {
--color-background: oklch(0.990 0.005 247.9);
--color-text: oklch(0.241 0.006 236.8);
--color-text-muted: oklch(0.459 0.008 255.5);
--color-axis: oklch(0.621 0.008 247.9);
--color-gridline: oklch(0.856 0.008 253.9);
--color-series-1: oklch(0.421 0.142 255.6);
--color-series-2: oklch(0.6433 0.1323 82.79);
--color-series-3: oklch(0.403 0.112 46.6);
--color-series-4: oklch(0.640 0.171 299.9);
--color-series-5: oklch(0.640 0.090 170.2);
--color-series-6: oklch(0.520 0.090 89.9);
}
.dark {
--color-background: oklch(0.199 0.005 248.1);
--color-text: oklch(0.939 0.006 255.5);
--color-text-muted: oklch(0.760 0.008 260.7);
--color-axis: oklch(0.560 0.008 248.0);
--color-gridline: oklch(0.334 0.008 240.1);
--color-series-1: oklch(0.775 0.115 257.2);
--color-series-2: oklch(0.600 0.166 47.2);
--color-series-3: oklch(0.600 0.169 269.9);
--color-series-4: oklch(0.7206 0.1701 119.98);
--color-series-5: oklch(0.839 0.089 30.2);
--color-series-6: oklch(0.600 0.091 349.7);
}
}Tailwind CSS v4 @theme
@theme {
/* light */
--color-background: oklch(0.990 0.005 247.9); /* #f9fcff */
--color-text: oklch(0.241 0.006 236.8); /* #1d2022 */
--color-text-muted: oklch(0.459 0.008 255.5); /* #55585c */
--color-axis: oklch(0.621 0.008 247.9); /* #83878b */
--color-gridline: oklch(0.856 0.008 253.9); /* #ccd0d5 */
--color-series-1: oklch(0.421 0.142 255.6); /* #004b98 */
--color-series-2: oklch(0.6433 0.1323 82.79); /* #b48400 */
--color-series-3: oklch(0.403 0.112 46.6); /* #773000 */
--color-series-4: oklch(0.640 0.171 299.9); /* #9d6fe3 */
--color-series-5: oklch(0.640 0.090 170.2); /* #4c9e84 */
--color-series-6: oklch(0.520 0.090 89.9); /* #7d6622 */
/* dark: use as dark:bg-background-dark, or remap under a .dark selector */
--color-background-dark: oklch(0.199 0.005 248.1); /* #141618 */
--color-text-dark: oklch(0.939 0.006 255.5); /* #e8ebef */
--color-text-muted-dark: oklch(0.760 0.008 260.7); /* #aeb1b6 */
--color-axis-dark: oklch(0.560 0.008 248.0); /* #717579 */
--color-gridline-dark: oklch(0.334 0.008 240.1); /* #33373a */
--color-series-1-dark: oklch(0.775 0.115 257.2); /* #86b8ff */
--color-series-2-dark: oklch(0.600 0.166 47.2); /* #cc5900 */
--color-series-3-dark: oklch(0.600 0.169 269.9); /* #5a76e4 */
--color-series-4-dark: oklch(0.7206 0.1701 119.98); /* #98b305 */
--color-series-5-dark: oklch(0.839 0.089 30.2); /* #ffb5a8 */
--color-series-6-dark: oklch(0.600 0.091 349.7); /* #a96988 */
}shadcn/ui variables (:root and .dark)
:root {
--background: oklch(0.990 0.005 247.9);
--foreground: oklch(0.241 0.006 236.8);
--muted-foreground: oklch(0.459 0.008 255.5);
--border: oklch(0.856 0.008 253.9);
--input: oklch(0.621 0.008 247.9);
--chart-1: oklch(0.421 0.142 255.6);
--chart-2: oklch(0.6433 0.1323 82.79);
--chart-3: oklch(0.640 0.171 299.9);
--chart-4: oklch(0.640 0.090 170.2);
--chart-5: oklch(0.520 0.090 89.9);
}
.dark {
--background: oklch(0.199 0.005 248.1);
--foreground: oklch(0.939 0.006 255.5);
--muted-foreground: oklch(0.760 0.008 260.7);
--border: oklch(0.334 0.008 240.1);
--input: oklch(0.560 0.008 248.0);
--chart-1: oklch(0.775 0.115 257.2);
--chart-2: oklch(0.600 0.166 47.2);
--chart-3: oklch(0.7206 0.1701 119.98);
--chart-4: oklch(0.839 0.089 30.2);
--chart-5: oklch(0.600 0.091 349.7);
}Design tokens (DTCG-style JSON)
{
"color": {
"light": {
"background": {
"$type": "color",
"$value": "oklch(0.990 0.005 247.9)",
"$description": "sRGB #f9fcff"
},
"text": {
"$type": "color",
"$value": "oklch(0.241 0.006 236.8)",
"$description": "sRGB #1d2022"
},
"text-muted": {
"$type": "color",
"$value": "oklch(0.459 0.008 255.5)",
"$description": "sRGB #55585c"
},
"axis": {
"$type": "color",
"$value": "oklch(0.621 0.008 247.9)",
"$description": "sRGB #83878b"
},
"gridline": {
"$type": "color",
"$value": "oklch(0.856 0.008 253.9)",
"$description": "sRGB #ccd0d5"
},
"series-1": {
"$type": "color",
"$value": "oklch(0.421 0.142 255.6)",
"$description": "sRGB #004b98"
},
"series-2": {
"$type": "color",
"$value": "oklch(0.6433 0.1323 82.79)",
"$description": "sRGB #b48400"
},
"series-3": {
"$type": "color",
"$value": "oklch(0.403 0.112 46.6)",
"$description": "sRGB #773000"
},
"series-4": {
"$type": "color",
"$value": "oklch(0.640 0.171 299.9)",
"$description": "sRGB #9d6fe3"
},
"series-5": {
"$type": "color",
"$value": "oklch(0.640 0.090 170.2)",
"$description": "sRGB #4c9e84"
},
"series-6": {
"$type": "color",
"$value": "oklch(0.520 0.090 89.9)",
"$description": "sRGB #7d6622"
}
},
"dark": {
"background": {
"$type": "color",
"$value": "oklch(0.199 0.005 248.1)",
"$description": "sRGB #141618"
},
"text": {
"$type": "color",
"$value": "oklch(0.939 0.006 255.5)",
"$description": "sRGB #e8ebef"
},
"text-muted": {
"$type": "color",
"$value": "oklch(0.760 0.008 260.7)",
"$description": "sRGB #aeb1b6"
},
"axis": {
"$type": "color",
"$value": "oklch(0.560 0.008 248.0)",
"$description": "sRGB #717579"
},
"gridline": {
"$type": "color",
"$value": "oklch(0.334 0.008 240.1)",
"$description": "sRGB #33373a"
},
"series-1": {
"$type": "color",
"$value": "oklch(0.775 0.115 257.2)",
"$description": "sRGB #86b8ff"
},
"series-2": {
"$type": "color",
"$value": "oklch(0.600 0.166 47.2)",
"$description": "sRGB #cc5900"
},
"series-3": {
"$type": "color",
"$value": "oklch(0.600 0.169 269.9)",
"$description": "sRGB #5a76e4"
},
"series-4": {
"$type": "color",
"$value": "oklch(0.7206 0.1701 119.98)",
"$description": "sRGB #98b305"
},
"series-5": {
"$type": "color",
"$value": "oklch(0.839 0.089 30.2)",
"$description": "sRGB #ffb5a8"
},
"series-6": {
"$type": "color",
"$value": "oklch(0.600 0.091 349.7)",
"$description": "sRGB #a96988"
}
}
}
}Open in the studio
Take the first series colour into the picker, build a tonal scale from it, or check any pair of your own.
Open #004B98 in the OKLCH picker →Build a tonal scale →Check a pair
FAQ
- How is this different from the eight-colour palette?
- It has fewer colours and a higher threshold, 11 instead of 8, so every pair is further apart. The eight-colour set maximises count; this one maximises separation.
- Does it work for achromatopsia?
- Only partly. The colours differ in lightness, but with no colour perception similar-lightness pairs can merge. Patterns and labels are the dependable answer. The colour blindness page previews a text and background pair in luminance only.
- Can I use it for maps?
- For categorical map regions, yes, up to six classes. For values that increase, use the sequential palette.
- Is the Machado simulation accurate?
- It is a widely used model of dichromacy, not a measurement of any one person's vision. Treat the numbers as a strong screening signal.