OKLCH Studio · Accessible palettes · Special needs
High contrast accessible colour palette for low vision (WCAG AAA)
Updated 2026-10-19
Low vision covers a wide range: blurred sight, tunnel vision, light sensitivity, reduced contrast sensitivity and more. What many of these share is that faint differences are lost first. Pale grey text, thin light borders and subtle hover tints, all common in current interface design, may simply disappear.
This palette sets every text pair at WCAG AAA, 7:1 or better, including muted text, links, buttons and status messages. Borders and focus rings are held higher than the 3:1 that WCAG 1.4.11 requires, and the near-black on near-white pair is used for body text. The result is plain, but it is readable.
AAA is not a level that WCAG recommends for whole sites in every case, because it can be impossible for some content, but for a mode or a product aimed at low-vision users it is a reasonable target. Operating-system high-contrast and forced-colours modes may override your colours entirely, so also test with those.
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.
Appointment confirmed
Secondary text sits on the surface colour.
Link in primary · Accent text
Light theme
- Background
#FDFDFD
oklch(0.994 0.000 0.0)
Sample text
- Surface
#FEFEFE
oklch(0.997 0.000 0.0)
Sample text
- Text
#1B1B1B
oklch(0.222 0.000 0.0)
Sample text
- Muted text
#575757
oklch(0.457 0.000 0.0)
Sample text
- Primary
#214AC8
oklch(0.467 0.201 265.2)
Sample text
- Primary foreground
#FCFCFC
oklch(0.991 0.000 0.0)
Sample text
- Accent
#9A3700
oklch(0.473 0.143 41.8)
Sample text
- Border
#929292
oklch(0.660 0.000 0.0)
Sample text
- Focus ring
#1C45C2
oklch(0.451 0.200 264.9)
Sample text
- Success
#006263
oklch(0.450 0.077 196.0)
Sample text
- Success fill
#006164
oklch(0.447 0.076 198.5)
Sample text
- Success fill label
#FCFCFC
oklch(0.991 0.000 0.0)
Sample text
- Warning
#844800
oklch(0.465 0.109 60.4)
Sample text
- Warning fill
#FFBA44
oklch(0.8335 0.1508 76.88)
Sample text
- Warning fill label
#0F0F0F
oklch(0.168 0.000 0.0)
Sample text
- Error
#A21A1B
oklch(0.4603 0.1706 26.95)
Sample text
- Error fill
#AE1117
oklch(0.478 0.186 27.0)
Sample text
- Error fill label
#FCFCFC
oklch(0.991 0.000 0.0)
Sample text
- Info
#005B94
oklch(0.457 0.118 246.1)
Sample text
- Info fill
#005999
oklch(0.455 0.126 249.2)
Sample text
- Info fill label
#FCFCFC
oklch(0.991 0.000 0.0)
Sample text
Appointment confirmed
Secondary text sits on the surface colour.
Link in primary · Accent text
Dark theme
- Background
#060606
oklch(0.122 0.000 0.0)
Sample text
- Surface
#0E0E0E
oklch(0.164 0.000 0.0)
Sample text
- Text
#EEEEEE
oklch(0.949 0.000 0.0)
Sample text
- Muted text
#BEBEBE
oklch(0.802 0.000 0.0)
Sample text
- Primary
#7EB6FF
oklch(0.767 0.121 255.4)
Sample text
- Primary foreground
#0D0D0D
oklch(0.159 0.000 0.0)
Sample text
- Accent
#FFA63D
oklch(0.797 0.155 66.0)
Sample text
- Border
#636363
oklch(0.500 0.000 0.0)
Sample text
- Focus ring
#7EB6FF
oklch(0.767 0.121 255.4)
Sample text
- Success
#55E3E3
oklch(0.8406 0.1202 195.11)
Sample text
- Success fill
#00CACB
oklch(0.760 0.130 195.4)
Sample text
- Success fill label
#0F0F0F
oklch(0.168 0.000 0.0)
Sample text
- Warning
#FFBC56
oklch(0.839 0.140 75.1)
Sample text
- Warning fill
#E9A11D
oklch(0.7605 0.1539 75.30)
Sample text
- Warning fill label
#0F0F0F
oklch(0.168 0.000 0.0)
Sample text
- Error
#FB7266
oklch(0.714 0.170 27.2)
Sample text
- Error fill
#FF7A6D
oklch(0.732 0.165 27.5)
Sample text
- Error fill label
#0F0F0F
oklch(0.168 0.000 0.0)
Sample text
- Info
#71BFFF
oklch(0.780 0.120 245.0)
Sample text
- Info fill
#61B9FF
oklch(0.760 0.132 244.8)
Sample text
- Info fill label
#0F0F0F
oklch(0.168 0.000 0.0)
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 |
|---|---|---|---|---|---|---|---|
| Text on background#1b1b1b on #fdfdfd | 16.93:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +102.9 | Lc 75: body text | Open in checker |
| Text on surface#1b1b1b on #fefefe | 17.07:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +103.5 | Lc 75: body text | Open in checker |
| Muted text on background#575757 on #fdfdfd | 7.10:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +83.9 | Lc 75: body text | Open in checker |
| Muted text on surface#575757 on #fefefe | 7.16:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +84.5 | Lc 75: body text | Open in checker |
| Primary as link text on background#214ac8 on #fdfdfd | 7.17:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +83.4 | Lc 75: body text | Open in checker |
| Primary as link text on surface#214ac8 on #fefefe | 7.23:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +84.0 | Lc 75: body text | Open in checker |
| Button label on primary#fcfcfc on #214ac8 | 7.11:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −87.5 | Lc 75: body text | Open in checker |
| Accent text on background#9a3700 on #fdfdfd | 7.06:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +82.8 | Lc 75: body text | Open in checker |
| Accent text on surface#9a3700 on #fefefe | 7.12:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +83.4 | Lc 75: body text | Open in checker |
| Border on background#929292 on #fdfdfd | 3.05:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | +56.9 | Lc 45: headlines and large text | Open in checker |
| Border on surface#929292 on #fefefe | 3.08:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | +57.5 | Lc 45: headlines and large text | Open in checker |
| Focus ring on background#1c45c2 on #fdfdfd | 7.70:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | +85.2 | Lc 75: body text | Open in checker |
| Focus ring on surface#1c45c2 on #fefefe | 7.77:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | +85.8 | Lc 75: body text | Open in checker |
| Success text on background#006263 on #fdfdfd | 7.05:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +83.2 | Lc 75: body text | Open in checker |
| Success text on surface#006263 on #fefefe | 7.11:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +83.8 | Lc 75: body text | Open in checker |
| Success badge label on fill#fcfcfc on #006164 | 7.07:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −87.6 | Lc 75: body text | Open in checker |
| Warning text on background#844800 on #fdfdfd | 7.09:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +83.4 | Lc 75: body text | Open in checker |
| Warning text on surface#844800 on #fefefe | 7.15:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +84.0 | Lc 75: body text | Open in checker |
| Warning badge label on fill#0f0f0f on #ffba44 | 11.28:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +73.2 | Lc 60: larger content text | Open in checker |
| Error text on background#a21a1b on #fdfdfd | 7.64:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +84.1 | Lc 75: body text | Open in checker |
| Error text on surface#a21a1b on #fefefe | 7.71:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +84.7 | Lc 75: body text | Open in checker |
| Error badge label on fill#fcfcfc on #ae1117 | 7.07:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −86.1 | Lc 75: body text | Open in checker |
| Info text on background#005b94 on #fdfdfd | 7.06:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +83.1 | Lc 75: body text | Open in checker |
| Info text on surface#005b94 on #fefefe | 7.12:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +83.7 | Lc 75: body text | Open in checker |
| Info badge label on fill#fcfcfc on #005999 | 7.08:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −87.5 | Lc 75: body text | Open in checker |
| Pair | WCAG ratio | Target | Normal AA / AAA | Large AA / AAA | APCA Lc | APCA tier | Check |
|---|---|---|---|---|---|---|---|
| Text on background#eeeeee on #060606 | 17.46:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −96.7 | Lc 75: body text | Open in checker |
| Text on surface#eeeeee on #0e0e0e | 16.63:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −96.4 | Lc 75: body text | Open in checker |
| Muted text on background#bebebe on #060606 | 10.90:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −67.4 | Lc 60: larger content text | Open in checker |
| Muted text on surface#bebebe on #0e0e0e | 10.38:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −67.1 | Lc 60: larger content text | Open in checker |
| Primary as link text on background#7eb6ff on #060606 | 9.66:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −61.4 | Lc 60: larger content text | Open in checker |
| Primary as link text on surface#7eb6ff on #0e0e0e | 9.21:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −61.1 | Lc 60: larger content text | Open in checker |
| Button label on primary#0d0d0d on #7eb6ff | 9.27:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +62.7 | Lc 60: larger content text | Open in checker |
| Accent text on background#ffa63d on #060606 | 10.39:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −65.3 | Lc 60: larger content text | Open in checker |
| Accent text on surface#ffa63d on #0e0e0e | 9.90:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −65.0 | Lc 60: larger content text | Open in checker |
| Border on background#636363 on #060606 | 3.37:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | −21.7 | Below Lc 30 | Open in checker |
| Border on surface#636363 on #0e0e0e | 3.21:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | −21.5 | Below Lc 30 | Open in checker |
| Focus ring on background#7eb6ff on #060606 | 9.66:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | −61.4 | Lc 60: larger content text | Open in checker |
| Focus ring on surface#7eb6ff on #0e0e0e | 9.21:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | −61.1 | Lc 60: larger content text | Open in checker |
| Success text on background#55e3e3 on #060606 | 13.00:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −77.8 | Lc 75: body text | Open in checker |
| Success text on surface#55e3e3 on #0e0e0e | 12.39:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −77.5 | Lc 75: body text | Open in checker |
| Success badge label on fill#0f0f0f on #00cacb | 9.41:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +64.5 | Lc 60: larger content text | Open in checker |
| Warning text on background#ffbc56 on #060606 | 12.13:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −73.6 | Lc 60: larger content text | Open in checker |
| Warning text on surface#ffbc56 on #0e0e0e | 11.56:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −73.3 | Lc 60: larger content text | Open in checker |
| Warning badge label on fill#0f0f0f on #e9a11d | 8.74:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +60.6 | Lc 60: larger content text | Open in checker |
| Error text on background#fb7266 on #060606 | 7.43:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −49.7 | Lc 45: headlines and large text | Open in checker |
| Error text on surface#fb7266 on #0e0e0e | 7.07:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −49.4 | Lc 45: headlines and large text | Open in checker |
| Error badge label on fill#0f0f0f on #ff7a6d | 7.53:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +54.2 | Lc 45: headlines and large text | Open in checker |
| Info text on background#71bfff on #060606 | 10.22:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −64.3 | Lc 60: larger content text | Open in checker |
| Info text on surface#71bfff on #0e0e0e | 9.74:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −64.0 | Lc 60: larger content text | Open in checker |
| Info badge label on fill#0f0f0f on #61b9ff | 9.02:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +62.1 | Lc 60: larger content text | Open in checker |
Status colours and colour blindness
The four status fills were simulated for protanopia, deuteranopia and tritanopia (Machado et al., 2009). Success against error, the pair most often confused, must stay at least 6 ΔE-OK ×100 apart under every simulation. The other pairs are shown for information, so keep an icon and a word on every status.
| Pair | Normal vision | Protanopia | Deuteranopia | Tritanopia | Held to 6 |
|---|---|---|---|---|---|
| success / warning | 43.6 | 37.3 | 48.7 | 38.6 | Shown only |
| success / error | 26.4 | 20.0 | 11.9 | 31.1 | Yes |
| success / info | 9.8 | 9.6 | 9.0 | 1.8 | Shown only |
| warning / error | 38.4 | 54.2 | 48.8 | 27.7 | Shown only |
| warning / info | 46.8 | 41.3 | 51.7 | 38.3 | Shown only |
| error / info | 29.3 | 25.2 | 20.8 | 31.2 | Shown only |
Fills as simulated:Normal visionProtanopiaDeuteranopiaTritanopia
| Pair | Normal vision | Protanopia | Deuteranopia | Tritanopia | Held to 6 |
|---|---|---|---|---|---|
| success / warning | 24.6 | 18.6 | 26.4 | 28.5 | Shown only |
| success / error | 29.4 | 16.2 | 16.6 | 32.6 | Yes |
| success / info | 10.9 | 7.3 | 8.1 | 4.9 | Shown only |
| warning / error | 13.3 | 13.2 | 10.5 | 5.5 | Shown only |
| warning / info | 28.5 | 26.0 | 28.7 | 26.2 | Shown only |
| error / info | 28.3 | 21.8 | 20.8 | 29.7 | Shown only |
Fills as simulated:Normal visionProtanopiaDeuteranopiaTritanopia
Why these colours
Neutrals have no tint and the page is near-white at lightness 0.995 in the light theme and near-black at 0.12 in the dark theme, which widens the range available to every other role. The primary is a pure, strong blue at OKLCH hue 265 and chroma 0.20 that the solver darkens until white labels reach 7:1.
The accent is an amber-orange at hue 60, and the status roles are all AAA as well. Success was moved toward cyan at hue 195 so that it stays apart from the red-orange error for red-green colour blindness even after both are darkened to reach 7:1. Darkened colours converge in lightness, and the page reports their separation under simulation.
Remember that the largest audience for a high-contrast option is not only people with a diagnosed condition. Many people read in strong sunlight, on old screens or while tired, and benefit from the same margin. Offering the palette as a visible choice, rather than hiding it in settings, makes it easy to find for those who need it in the moment.
The generator solved 50 pairs (25 light, 25 dark), and all of them meet their target on the raw WCAG ratio. The tightest is success text on background in the light theme at 7.05:1 against a target of 7:1. After gamut mapping to sRGB the light primary keeps 100% of the requested chroma; hue was held throughout.
How to use it
- Use real borders on inputs and buttons, not tints, so every control has a visible edge.
- Make the focus indicator thick, at least 2 px, with an offset, and keep it in the focus ring colour.
- Keep links underlined and avoid relying on colour changes for hover.
- Let users enlarge text to 200 percent without loss, and avoid fixed-height containers.
- Test with forced-colors mode and the system high-contrast themes, where your CSS colours may be replaced.
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: #fdfdfd;
--color-surface: #fefefe;
--color-text: #1b1b1b;
--color-text-muted: #575757;
--color-primary: #214ac8;
--color-primary-foreground: #fcfcfc;
--color-accent: #9a3700;
--color-border: #929292;
--color-focus-ring: #1c45c2;
--color-success: #006263;
--color-success-solid: #006164;
--color-success-solid-foreground: #fcfcfc;
--color-warning: #844800;
--color-warning-solid: #ffba44;
--color-warning-solid-foreground: #0f0f0f;
--color-error: #a21a1b;
--color-error-solid: #ae1117;
--color-error-solid-foreground: #fcfcfc;
--color-info: #005b94;
--color-info-solid: #005999;
--color-info-solid-foreground: #fcfcfc;
}
.dark {
--color-background: #060606;
--color-surface: #0e0e0e;
--color-text: #eeeeee;
--color-text-muted: #bebebe;
--color-primary: #7eb6ff;
--color-primary-foreground: #0d0d0d;
--color-accent: #ffa63d;
--color-border: #636363;
--color-focus-ring: #7eb6ff;
--color-success: #55e3e3;
--color-success-solid: #00cacb;
--color-success-solid-foreground: #0f0f0f;
--color-warning: #ffbc56;
--color-warning-solid: #e9a11d;
--color-warning-solid-foreground: #0f0f0f;
--color-error: #fb7266;
--color-error-solid: #ff7a6d;
--color-error-solid-foreground: #0f0f0f;
--color-info: #71bfff;
--color-info-solid: #61b9ff;
--color-info-solid-foreground: #0f0f0f;
}
/* oklch() where supported: the same colours, unclipped values of the hex above. */
@supports (color: oklch(0 0 0)) {
:root {
--color-background: oklch(0.994 0.000 0.0);
--color-surface: oklch(0.997 0.000 0.0);
--color-text: oklch(0.222 0.000 0.0);
--color-text-muted: oklch(0.457 0.000 0.0);
--color-primary: oklch(0.467 0.201 265.2);
--color-primary-foreground: oklch(0.991 0.000 0.0);
--color-accent: oklch(0.473 0.143 41.8);
--color-border: oklch(0.660 0.000 0.0);
--color-focus-ring: oklch(0.451 0.200 264.9);
--color-success: oklch(0.450 0.077 196.0);
--color-success-solid: oklch(0.447 0.076 198.5);
--color-success-solid-foreground: oklch(0.991 0.000 0.0);
--color-warning: oklch(0.465 0.109 60.4);
--color-warning-solid: oklch(0.8335 0.1508 76.88);
--color-warning-solid-foreground: oklch(0.168 0.000 0.0);
--color-error: oklch(0.4603 0.1706 26.95);
--color-error-solid: oklch(0.478 0.186 27.0);
--color-error-solid-foreground: oklch(0.991 0.000 0.0);
--color-info: oklch(0.457 0.118 246.1);
--color-info-solid: oklch(0.455 0.126 249.2);
--color-info-solid-foreground: oklch(0.991 0.000 0.0);
}
.dark {
--color-background: oklch(0.122 0.000 0.0);
--color-surface: oklch(0.164 0.000 0.0);
--color-text: oklch(0.949 0.000 0.0);
--color-text-muted: oklch(0.802 0.000 0.0);
--color-primary: oklch(0.767 0.121 255.4);
--color-primary-foreground: oklch(0.159 0.000 0.0);
--color-accent: oklch(0.797 0.155 66.0);
--color-border: oklch(0.500 0.000 0.0);
--color-focus-ring: oklch(0.767 0.121 255.4);
--color-success: oklch(0.8406 0.1202 195.11);
--color-success-solid: oklch(0.760 0.130 195.4);
--color-success-solid-foreground: oklch(0.168 0.000 0.0);
--color-warning: oklch(0.839 0.140 75.1);
--color-warning-solid: oklch(0.7605 0.1539 75.30);
--color-warning-solid-foreground: oklch(0.168 0.000 0.0);
--color-error: oklch(0.714 0.170 27.2);
--color-error-solid: oklch(0.732 0.165 27.5);
--color-error-solid-foreground: oklch(0.168 0.000 0.0);
--color-info: oklch(0.780 0.120 245.0);
--color-info-solid: oklch(0.760 0.132 244.8);
--color-info-solid-foreground: oklch(0.168 0.000 0.0);
}
}Tailwind CSS v4 @theme
@theme {
/* light */
--color-background: oklch(0.994 0.000 0.0); /* #fdfdfd */
--color-surface: oklch(0.997 0.000 0.0); /* #fefefe */
--color-text: oklch(0.222 0.000 0.0); /* #1b1b1b */
--color-text-muted: oklch(0.457 0.000 0.0); /* #575757 */
--color-primary: oklch(0.467 0.201 265.2); /* #214ac8 */
--color-primary-foreground: oklch(0.991 0.000 0.0); /* #fcfcfc */
--color-accent: oklch(0.473 0.143 41.8); /* #9a3700 */
--color-border: oklch(0.660 0.000 0.0); /* #929292 */
--color-focus-ring: oklch(0.451 0.200 264.9); /* #1c45c2 */
--color-success: oklch(0.450 0.077 196.0); /* #006263 */
--color-success-solid: oklch(0.447 0.076 198.5); /* #006164 */
--color-success-solid-foreground: oklch(0.991 0.000 0.0); /* #fcfcfc */
--color-warning: oklch(0.465 0.109 60.4); /* #844800 */
--color-warning-solid: oklch(0.8335 0.1508 76.88); /* #ffba44 */
--color-warning-solid-foreground: oklch(0.168 0.000 0.0); /* #0f0f0f */
--color-error: oklch(0.4603 0.1706 26.95); /* #a21a1b */
--color-error-solid: oklch(0.478 0.186 27.0); /* #ae1117 */
--color-error-solid-foreground: oklch(0.991 0.000 0.0); /* #fcfcfc */
--color-info: oklch(0.457 0.118 246.1); /* #005b94 */
--color-info-solid: oklch(0.455 0.126 249.2); /* #005999 */
--color-info-solid-foreground: oklch(0.991 0.000 0.0); /* #fcfcfc */
/* dark: use as dark:bg-background-dark, or remap under a .dark selector */
--color-background-dark: oklch(0.122 0.000 0.0); /* #060606 */
--color-surface-dark: oklch(0.164 0.000 0.0); /* #0e0e0e */
--color-text-dark: oklch(0.949 0.000 0.0); /* #eeeeee */
--color-text-muted-dark: oklch(0.802 0.000 0.0); /* #bebebe */
--color-primary-dark: oklch(0.767 0.121 255.4); /* #7eb6ff */
--color-primary-foreground-dark: oklch(0.159 0.000 0.0); /* #0d0d0d */
--color-accent-dark: oklch(0.797 0.155 66.0); /* #ffa63d */
--color-border-dark: oklch(0.500 0.000 0.0); /* #636363 */
--color-focus-ring-dark: oklch(0.767 0.121 255.4); /* #7eb6ff */
--color-success-dark: oklch(0.8406 0.1202 195.11); /* #55e3e3 */
--color-success-solid-dark: oklch(0.760 0.130 195.4); /* #00cacb */
--color-success-solid-foreground-dark: oklch(0.168 0.000 0.0); /* #0f0f0f */
--color-warning-dark: oklch(0.839 0.140 75.1); /* #ffbc56 */
--color-warning-solid-dark: oklch(0.7605 0.1539 75.30); /* #e9a11d */
--color-warning-solid-foreground-dark: oklch(0.168 0.000 0.0); /* #0f0f0f */
--color-error-dark: oklch(0.714 0.170 27.2); /* #fb7266 */
--color-error-solid-dark: oklch(0.732 0.165 27.5); /* #ff7a6d */
--color-error-solid-foreground-dark: oklch(0.168 0.000 0.0); /* #0f0f0f */
--color-info-dark: oklch(0.780 0.120 245.0); /* #71bfff */
--color-info-solid-dark: oklch(0.760 0.132 244.8); /* #61b9ff */
--color-info-solid-foreground-dark: oklch(0.168 0.000 0.0); /* #0f0f0f */
}shadcn/ui variables (:root and .dark)
:root {
--background: oklch(0.994 0.000 0.0);
--foreground: oklch(0.222 0.000 0.0);
--card: oklch(0.997 0.000 0.0);
--card-foreground: oklch(0.222 0.000 0.0);
--popover: oklch(0.997 0.000 0.0);
--popover-foreground: oklch(0.222 0.000 0.0);
--primary: oklch(0.467 0.201 265.2);
--primary-foreground: oklch(0.991 0.000 0.0);
--secondary: oklch(0.997 0.000 0.0);
--secondary-foreground: oklch(0.222 0.000 0.0);
--muted: oklch(0.997 0.000 0.0);
--muted-foreground: oklch(0.457 0.000 0.0);
--accent: oklch(0.997 0.000 0.0);
--accent-foreground: oklch(0.222 0.000 0.0);
--destructive: oklch(0.478 0.186 27.0);
--destructive-foreground: oklch(0.991 0.000 0.0);
--border: oklch(0.660 0.000 0.0);
--input: oklch(0.660 0.000 0.0);
--ring: oklch(0.451 0.200 264.9);
--success: oklch(0.447 0.076 198.5);
--success-foreground: oklch(0.991 0.000 0.0);
--warning: oklch(0.8335 0.1508 76.88);
--warning-foreground: oklch(0.168 0.000 0.0);
--info: oklch(0.455 0.126 249.2);
--info-foreground: oklch(0.991 0.000 0.0);
--brand-accent: oklch(0.473 0.143 41.8);
}
.dark {
--background: oklch(0.122 0.000 0.0);
--foreground: oklch(0.949 0.000 0.0);
--card: oklch(0.164 0.000 0.0);
--card-foreground: oklch(0.949 0.000 0.0);
--popover: oklch(0.164 0.000 0.0);
--popover-foreground: oklch(0.949 0.000 0.0);
--primary: oklch(0.767 0.121 255.4);
--primary-foreground: oklch(0.159 0.000 0.0);
--secondary: oklch(0.164 0.000 0.0);
--secondary-foreground: oklch(0.949 0.000 0.0);
--muted: oklch(0.164 0.000 0.0);
--muted-foreground: oklch(0.802 0.000 0.0);
--accent: oklch(0.164 0.000 0.0);
--accent-foreground: oklch(0.949 0.000 0.0);
--destructive: oklch(0.732 0.165 27.5);
--destructive-foreground: oklch(0.168 0.000 0.0);
--border: oklch(0.500 0.000 0.0);
--input: oklch(0.500 0.000 0.0);
--ring: oklch(0.767 0.121 255.4);
--success: oklch(0.760 0.130 195.4);
--success-foreground: oklch(0.168 0.000 0.0);
--warning: oklch(0.7605 0.1539 75.30);
--warning-foreground: oklch(0.168 0.000 0.0);
--info: oklch(0.760 0.132 244.8);
--info-foreground: oklch(0.168 0.000 0.0);
--brand-accent: oklch(0.797 0.155 66.0);
}Design tokens (DTCG-style JSON)
{
"color": {
"light": {
"background": {
"$type": "color",
"$value": "oklch(0.994 0.000 0.0)",
"$description": "sRGB #fdfdfd"
},
"surface": {
"$type": "color",
"$value": "oklch(0.997 0.000 0.0)",
"$description": "sRGB #fefefe"
},
"text": {
"$type": "color",
"$value": "oklch(0.222 0.000 0.0)",
"$description": "sRGB #1b1b1b"
},
"text-muted": {
"$type": "color",
"$value": "oklch(0.457 0.000 0.0)",
"$description": "sRGB #575757"
},
"primary": {
"$type": "color",
"$value": "oklch(0.467 0.201 265.2)",
"$description": "sRGB #214ac8"
},
"primary-foreground": {
"$type": "color",
"$value": "oklch(0.991 0.000 0.0)",
"$description": "sRGB #fcfcfc"
},
"accent": {
"$type": "color",
"$value": "oklch(0.473 0.143 41.8)",
"$description": "sRGB #9a3700"
},
"border": {
"$type": "color",
"$value": "oklch(0.660 0.000 0.0)",
"$description": "sRGB #929292"
},
"focus-ring": {
"$type": "color",
"$value": "oklch(0.451 0.200 264.9)",
"$description": "sRGB #1c45c2"
},
"success": {
"$type": "color",
"$value": "oklch(0.450 0.077 196.0)",
"$description": "sRGB #006263"
},
"success-solid": {
"$type": "color",
"$value": "oklch(0.447 0.076 198.5)",
"$description": "sRGB #006164"
},
"success-solid-foreground": {
"$type": "color",
"$value": "oklch(0.991 0.000 0.0)",
"$description": "sRGB #fcfcfc"
},
"warning": {
"$type": "color",
"$value": "oklch(0.465 0.109 60.4)",
"$description": "sRGB #844800"
},
"warning-solid": {
"$type": "color",
"$value": "oklch(0.8335 0.1508 76.88)",
"$description": "sRGB #ffba44"
},
"warning-solid-foreground": {
"$type": "color",
"$value": "oklch(0.168 0.000 0.0)",
"$description": "sRGB #0f0f0f"
},
"error": {
"$type": "color",
"$value": "oklch(0.4603 0.1706 26.95)",
"$description": "sRGB #a21a1b"
},
"error-solid": {
"$type": "color",
"$value": "oklch(0.478 0.186 27.0)",
"$description": "sRGB #ae1117"
},
"error-solid-foreground": {
"$type": "color",
"$value": "oklch(0.991 0.000 0.0)",
"$description": "sRGB #fcfcfc"
},
"info": {
"$type": "color",
"$value": "oklch(0.457 0.118 246.1)",
"$description": "sRGB #005b94"
},
"info-solid": {
"$type": "color",
"$value": "oklch(0.455 0.126 249.2)",
"$description": "sRGB #005999"
},
"info-solid-foreground": {
"$type": "color",
"$value": "oklch(0.991 0.000 0.0)",
"$description": "sRGB #fcfcfc"
}
},
"dark": {
"background": {
"$type": "color",
"$value": "oklch(0.122 0.000 0.0)",
"$description": "sRGB #060606"
},
"surface": {
"$type": "color",
"$value": "oklch(0.164 0.000 0.0)",
"$description": "sRGB #0e0e0e"
},
"text": {
"$type": "color",
"$value": "oklch(0.949 0.000 0.0)",
"$description": "sRGB #eeeeee"
},
"text-muted": {
"$type": "color",
"$value": "oklch(0.802 0.000 0.0)",
"$description": "sRGB #bebebe"
},
"primary": {
"$type": "color",
"$value": "oklch(0.767 0.121 255.4)",
"$description": "sRGB #7eb6ff"
},
"primary-foreground": {
"$type": "color",
"$value": "oklch(0.159 0.000 0.0)",
"$description": "sRGB #0d0d0d"
},
"accent": {
"$type": "color",
"$value": "oklch(0.797 0.155 66.0)",
"$description": "sRGB #ffa63d"
},
"border": {
"$type": "color",
"$value": "oklch(0.500 0.000 0.0)",
"$description": "sRGB #636363"
},
"focus-ring": {
"$type": "color",
"$value": "oklch(0.767 0.121 255.4)",
"$description": "sRGB #7eb6ff"
},
"success": {
"$type": "color",
"$value": "oklch(0.8406 0.1202 195.11)",
"$description": "sRGB #55e3e3"
},
"success-solid": {
"$type": "color",
"$value": "oklch(0.760 0.130 195.4)",
"$description": "sRGB #00cacb"
},
"success-solid-foreground": {
"$type": "color",
"$value": "oklch(0.168 0.000 0.0)",
"$description": "sRGB #0f0f0f"
},
"warning": {
"$type": "color",
"$value": "oklch(0.839 0.140 75.1)",
"$description": "sRGB #ffbc56"
},
"warning-solid": {
"$type": "color",
"$value": "oklch(0.7605 0.1539 75.30)",
"$description": "sRGB #e9a11d"
},
"warning-solid-foreground": {
"$type": "color",
"$value": "oklch(0.168 0.000 0.0)",
"$description": "sRGB #0f0f0f"
},
"error": {
"$type": "color",
"$value": "oklch(0.714 0.170 27.2)",
"$description": "sRGB #fb7266"
},
"error-solid": {
"$type": "color",
"$value": "oklch(0.732 0.165 27.5)",
"$description": "sRGB #ff7a6d"
},
"error-solid-foreground": {
"$type": "color",
"$value": "oklch(0.168 0.000 0.0)",
"$description": "sRGB #0f0f0f"
},
"info": {
"$type": "color",
"$value": "oklch(0.780 0.120 245.0)",
"$description": "sRGB #71bfff"
},
"info-solid": {
"$type": "color",
"$value": "oklch(0.760 0.132 244.8)",
"$description": "sRGB #61b9ff"
},
"info-solid-foreground": {
"$type": "color",
"$value": "oklch(0.168 0.000 0.0)",
"$description": "sRGB #0f0f0f"
}
}
}
}Open in the studio
Take the primary colour into the picker, build a tonal scale from it, or check any pair of your own.
Open #214AC8 in the OKLCH picker →Build a tonal scale →Check a pair
FAQ
- Does high contrast mean pure black on pure white?
- Not necessarily. Some users with light sensitivity find the maximum contrast uncomfortable. This palette lets the solver choose the nearest passing value, and offers a dark theme.
- Is 7:1 required by WCAG?
- No. AA, 4.5:1, is the usual requirement; 7:1 is the AAA level in Success Criterion 1.4.6. Targeting it is optional.
- How does this relate to the dark theme?
- The dark theme uses a near-black background and light text with the same AAA targets, so users who prefer light-on-dark also get 7:1 pairs.
- What about the APCA numbers?
- They are listed beside each WCAG ratio. Most text pairs reach the Lc 75 body-text tier or beyond in the light theme, and dark pairs show negative signs.