OKLCH Studio · Accessible palettes · Product & UI
Accessible color palette for dark mode (WCAG 2.2 verified)
Updated 2026-10-19
Dark mode is not a colour inversion. Light text on a dark background behaves differently from dark text on light: the WCAG 2.x ratio is symmetric, but eyes and screens are not. Very bright text on a very dark ground can appear to bloom, and saturated colours that look fine on white can vibrate on black. Many dark themes fail their own contrast checks in the status and muted colours.
This palette is dark-first: the dark theme is shown before the light one, and every role is solved against the dark background and the raised surface. Body text is targeted at AAA, muted text and interactive colours at AA, and borders and focus rings at 3:1. A light theme is included so the same tokens can be used for a system-driven switch.
WCAG 2.2 AA is the baseline most policies cite and it applies equally to both themes. APCA, listed beside the WCAG ratio, is helpful here because its model treats light-on-dark differently; the signed Lc values are negative for light text on a dark background.
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
Dark theme
- Background
#0B1015
oklch(0.170 0.013 248.6)
Sample text
- Surface
#151A1F
oklch(0.215 0.012 248.4)
Sample text
- Text
#E7EFF8
oklch(0.949 0.015 251.2)
Sample text
- Muted text
#B5BFC9
oklch(0.800 0.018 248.1)
Sample text
- Primary
#7AC3FF
oklch(0.793 0.113 244.7)
Sample text
- Primary foreground
#0C0D0F
oklch(0.159 0.005 264.4)
Sample text
- Accent
#89DA9B
oklch(0.820 0.119 150.2)
Sample text
- Border
#5F6770
oklch(0.510 0.017 251.3)
Sample text
- Focus ring
#7AC3FF
oklch(0.793 0.113 244.7)
Sample text
- Success
#6CE5B5
oklch(0.839 0.130 164.9)
Sample text
- Success fill
#3BCD9A
oklch(0.759 0.143 165.0)
Sample text
- Success fill label
#0E1011
oklch(0.171 0.004 229.1)
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
#0E1011
oklch(0.171 0.004 229.1)
Sample text
- Error
#F66D62
oklch(0.700 0.171 27.0)
Sample text
- Error fill
#FF7A6D
oklch(0.732 0.165 27.5)
Sample text
- Error fill label
#0E1011
oklch(0.171 0.004 229.1)
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
#0E1011
oklch(0.171 0.004 229.1)
Sample text
Appointment confirmed
Secondary text sits on the surface colour.
Link in primary · Accent text
Light theme
- Background
#F1F8FF
oklch(0.976 0.012 247.9)
Sample text
- Surface
#F8FFFF
oklch(0.995 0.007 197.0)
Sample text
- Text
#161B21
oklch(0.220 0.014 253.1)
Sample text
- Muted text
#515962
oklch(0.460 0.018 251.3)
Sample text
- Primary
#1666AA
oklch(0.501 0.130 249.9)
Sample text
- Primary foreground
#F9FCFF
oklch(0.990 0.005 247.9)
Sample text
- Accent
#287C42
oklch(0.5212 0.1205 150.12)
Sample text
- Border
#868F99
oklch(0.646 0.018 250.9)
Sample text
- Focus ring
#00579A
oklch(0.451 0.130 250.6)
Sample text
- Success
#008158
oklch(0.5333 0.1163 162.37)
Sample text
- Success fill
#008457
oklch(0.5415 0.1211 160.71)
Sample text
- Success fill label
#F9FCFF
oklch(0.990 0.005 247.9)
Sample text
- Warning
#A16300
oklch(0.555 0.122 67.9)
Sample text
- Warning fill
#FFBA44
oklch(0.8335 0.1508 76.88)
Sample text
- Warning fill label
#0E1011
oklch(0.171 0.004 229.1)
Sample text
- Error
#A21A1B
oklch(0.4603 0.1706 26.95)
Sample text
- Error fill
#C8322E
oklch(0.551 0.187 27.1)
Sample text
- Error fill label
#F9FCFF
oklch(0.990 0.005 247.9)
Sample text
- Info
#2274AF
oklch(0.540 0.120 245.0)
Sample text
- Info fill
#1377B9
oklch(0.5501 0.1324 245.13)
Sample text
- Info fill label
#F9FCFF
oklch(0.990 0.005 247.9)
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#e7eff8 on #0b1015 | 16.46:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −96.3 | Lc 75: body text | Open in checker |
| Text on surface#e7eff8 on #151a1f | 15.09:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −95.5 | Lc 75: body text | Open in checker |
| Muted text on background#b5bfc9 on #0b1015 | 10.24:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −66.9 | Lc 60: larger content text | Open in checker |
| Muted text on surface#b5bfc9 on #151a1f | 9.38:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −66.0 | Lc 60: larger content text | Open in checker |
| Primary as link text on background#7ac3ff on #0b1015 | 10.07:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −66.2 | Lc 60: larger content text | Open in checker |
| Primary as link text on surface#7ac3ff on #151a1f | 9.23:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −65.3 | Lc 60: larger content text | Open in checker |
| Button label on primary#0c0d0f on #7ac3ff | 10.25:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +67.6 | Lc 60: larger content text | Open in checker |
| Accent text on background#89da9b on #0b1015 | 11.43:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −73.1 | Lc 60: larger content text | Open in checker |
| Accent text on surface#89da9b on #151a1f | 10.47:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −72.2 | Lc 60: larger content text | Open in checker |
| Border on background#5f6770 on #0b1015 | 3.33:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | −22.7 | Below Lc 30 | Open in checker |
| Border on surface#5f6770 on #151a1f | 3.05:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | −21.8 | Below Lc 30 | Open in checker |
| Focus ring on background#7ac3ff on #0b1015 | 10.07:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | −66.2 | Lc 60: larger content text | Open in checker |
| Focus ring on surface#7ac3ff on #151a1f | 9.23:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | −65.3 | Lc 60: larger content text | Open in checker |
| Success text on background#6ce5b5 on #0b1015 | 12.29:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −77.5 | Lc 75: body text | Open in checker |
| Success text on surface#6ce5b5 on #151a1f | 11.26:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −76.6 | Lc 75: body text | Open in checker |
| Success badge label on fill#0e1011 on #3bcd9a | 9.43:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +64.5 | Lc 60: larger content text | Open in checker |
| Warning text on background#ffbc56 on #0b1015 | 11.44:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −73.2 | Lc 60: larger content text | Open in checker |
| Warning text on surface#ffbc56 on #151a1f | 10.48:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −72.4 | Lc 60: larger content text | Open in checker |
| Warning badge label on fill#0e1011 on #e9a11d | 8.70:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +60.6 | Lc 60: larger content text | Open in checker |
| Error text on background#f66d62 on #0b1015 | 6.62:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −47.0 | Lc 45: headlines and large text | Open in checker |
| Error text on surface#f66d62 on #151a1f | 6.07:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −46.1 | Lc 45: headlines and large text | Open in checker |
| Error badge label on fill#0e1011 on #ff7a6d | 7.50:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +54.2 | Lc 45: headlines and large text | Open in checker |
| Info text on background#71bfff on #0b1015 | 9.64:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −63.9 | Lc 60: larger content text | Open in checker |
| Info text on surface#71bfff on #151a1f | 8.83:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −63.0 | Lc 60: larger content text | Open in checker |
| Info badge label on fill#0e1011 on #61b9ff | 8.98:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +62.0 | Lc 60: larger content text | Open in checker |
| Pair | WCAG ratio | Target | Normal AA / AAA | Large AA / AAA | APCA Lc | APCA tier | Check |
|---|---|---|---|---|---|---|---|
| Text on background#161b21 on #f1f8ff | 16.16:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +99.4 | Lc 75: body text | Open in checker |
| Text on surface#161b21 on #f8ffff | 17.09:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +103.3 | Lc 75: body text | Open in checker |
| Muted text on background#515962 on #f1f8ff | 6.63:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +79.9 | Lc 75: body text | Open in checker |
| Muted text on surface#515962 on #f8ffff | 7.01:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +83.8 | Lc 75: body text | Open in checker |
| Primary as link text on background#1666aa on #f1f8ff | 5.57:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +74.6 | Lc 60: larger content text | Open in checker |
| Primary as link text on surface#1666aa on #f8ffff | 5.90:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +78.5 | Lc 75: body text | Open in checker |
| Button label on primary#f9fcff on #1666aa | 5.80:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −82.3 | Lc 75: body text | Open in checker |
| Accent text on background#287c42 on #f1f8ff | 4.84:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +70.6 | Lc 60: larger content text | Open in checker |
| Accent text on surface#287c42 on #f8ffff | 5.11:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +74.5 | Lc 60: larger content text | Open in checker |
| Border on background#868f99 on #f1f8ff | 3.06:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | +55.4 | Lc 45: headlines and large text | Open in checker |
| Border on surface#868f99 on #f8ffff | 3.23:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | +59.3 | Lc 45: headlines and large text | Open in checker |
| Focus ring on background#00579a on #f1f8ff | 6.93:1 | 3:1 (WCAG 1.4.11) | Pass / Fail | Pass / Pass | +80.4 | Lc 75: body text | Open in checker |
| Focus ring on surface#00579a on #f8ffff | 7.32:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | +84.2 | Lc 75: body text | Open in checker |
| Success text on background#008158 on #f1f8ff | 4.58:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +68.6 | Lc 60: larger content text | Open in checker |
| Success text on surface#008158 on #f8ffff | 4.84:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +72.5 | Lc 60: larger content text | Open in checker |
| Success badge label on fill#f9fcff on #008457 | 4.59:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −75.4 | Lc 75: body text | Open in checker |
| Warning text on background#a16300 on #f1f8ff | 4.56:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +68.7 | Lc 60: larger content text | Open in checker |
| Warning text on surface#a16300 on #f8ffff | 4.82:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +72.6 | Lc 60: larger content text | Open in checker |
| Warning badge label on fill#0e1011 on #ffba44 | 11.22:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +73.1 | Lc 60: larger content text | Open in checker |
| Error text on background#a21a1b on #f1f8ff | 7.26:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +80.5 | Lc 75: body text | Open in checker |
| Error text on surface#a21a1b on #f8ffff | 7.68:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +84.4 | Lc 75: body text | Open in checker |
| Error badge label on fill#f9fcff on #c8322e | 5.16:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −78.1 | Lc 75: body text | Open in checker |
| Info text on background#2274af on #f1f8ff | 4.68:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +69.5 | Lc 60: larger content text | Open in checker |
| Info text on surface#2274af on #f8ffff | 4.95:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +73.4 | Lc 60: larger content text | Open in checker |
| Info badge label on fill#f9fcff on #1377b9 | 4.67:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −76.0 | Lc 75: body 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 | 20.9 | 11.8 | 15.5 | 27.5 | Shown only |
| success / error | 28.8 | 15.3 | 6.4 | 31.3 | Yes |
| success / info | 17.7 | 15.2 | 14.3 | 3.8 | 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
| Pair | Normal vision | Protanopia | Deuteranopia | Tritanopia | Held to 6 |
|---|---|---|---|---|---|
| success / warning | 34.5 | 26.3 | 39.2 | 31.9 | Shown only |
| success / error | 28.5 | 16.2 | 6.4 | 33.0 | Yes |
| success / info | 17.1 | 16.2 | 15.3 | 3.2 | Shown only |
| warning / error | 31.8 | 42.0 | 37.4 | 22.6 | Shown only |
| warning / info | 40.0 | 34.2 | 44.5 | 31.2 | Shown only |
| error / info | 30.3 | 24.1 | 21.6 | 33.0 | Shown only |
Fills as simulated:Normal visionProtanopiaDeuteranopiaTritanopia
Why these colours
The dark background sits at OKLCH lightness 0.17 with a blue tint at chroma 0.012, which keeps the page from turning flat grey. Raised surfaces are lighter, not darker, because in dark UIs elevation is shown by lightening. The text is a soft off-white, not pure white, solved to AAA against the lightest surface.
Primary and accent colours rise in lightness rather than chroma in dark mode. A blue at lightness 0.5 that works on white would be hard to see on near-black, so the solver raises lightness until the target is met while keeping hue and chroma. Chroma is held, and where the sRGB gamut cannot supply it at the new lightness, the gamut mapper reduces it slightly.
The generator solved 50 pairs (25 light, 25 dark), and all of them meet their target on the raw WCAG ratio. The tightest is warning text on background in the light theme at 4.56:1 against a target of 4.5:1. After gamut mapping to sRGB the light primary keeps 100% of the requested chroma; hue was held throughout.
How to use it
- Do not use pure black and pure white together for large text areas; the softened pair is easier on the eyes.
- Use elevation by lighter surfaces rather than shadows, which are invisible on dark backgrounds.
- Check images and logos for a dark variant; a dark logo on a dark header is a common failure.
- Respect prefers-color-scheme, and also offer a manual switch, since some people want a dark app in a bright room or the opposite.
- Test status colours in dark mode separately; light-mode reds and greens often lose contrast when reused.
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: #f1f8ff;
--color-surface: #f8ffff;
--color-text: #161b21;
--color-text-muted: #515962;
--color-primary: #1666aa;
--color-primary-foreground: #f9fcff;
--color-accent: #287c42;
--color-border: #868f99;
--color-focus-ring: #00579a;
--color-success: #008158;
--color-success-solid: #008457;
--color-success-solid-foreground: #f9fcff;
--color-warning: #a16300;
--color-warning-solid: #ffba44;
--color-warning-solid-foreground: #0e1011;
--color-error: #a21a1b;
--color-error-solid: #c8322e;
--color-error-solid-foreground: #f9fcff;
--color-info: #2274af;
--color-info-solid: #1377b9;
--color-info-solid-foreground: #f9fcff;
}
.dark {
--color-background: #0b1015;
--color-surface: #151a1f;
--color-text: #e7eff8;
--color-text-muted: #b5bfc9;
--color-primary: #7ac3ff;
--color-primary-foreground: #0c0d0f;
--color-accent: #89da9b;
--color-border: #5f6770;
--color-focus-ring: #7ac3ff;
--color-success: #6ce5b5;
--color-success-solid: #3bcd9a;
--color-success-solid-foreground: #0e1011;
--color-warning: #ffbc56;
--color-warning-solid: #e9a11d;
--color-warning-solid-foreground: #0e1011;
--color-error: #f66d62;
--color-error-solid: #ff7a6d;
--color-error-solid-foreground: #0e1011;
--color-info: #71bfff;
--color-info-solid: #61b9ff;
--color-info-solid-foreground: #0e1011;
}
/* oklch() where supported: the same colours, unclipped values of the hex above. */
@supports (color: oklch(0 0 0)) {
:root {
--color-background: oklch(0.976 0.012 247.9);
--color-surface: oklch(0.995 0.007 197.0);
--color-text: oklch(0.220 0.014 253.1);
--color-text-muted: oklch(0.460 0.018 251.3);
--color-primary: oklch(0.501 0.130 249.9);
--color-primary-foreground: oklch(0.990 0.005 247.9);
--color-accent: oklch(0.5212 0.1205 150.12);
--color-border: oklch(0.646 0.018 250.9);
--color-focus-ring: oklch(0.451 0.130 250.6);
--color-success: oklch(0.5333 0.1163 162.37);
--color-success-solid: oklch(0.5415 0.1211 160.71);
--color-success-solid-foreground: oklch(0.990 0.005 247.9);
--color-warning: oklch(0.555 0.122 67.9);
--color-warning-solid: oklch(0.8335 0.1508 76.88);
--color-warning-solid-foreground: oklch(0.171 0.004 229.1);
--color-error: oklch(0.4603 0.1706 26.95);
--color-error-solid: oklch(0.551 0.187 27.1);
--color-error-solid-foreground: oklch(0.990 0.005 247.9);
--color-info: oklch(0.540 0.120 245.0);
--color-info-solid: oklch(0.5501 0.1324 245.13);
--color-info-solid-foreground: oklch(0.990 0.005 247.9);
}
.dark {
--color-background: oklch(0.170 0.013 248.6);
--color-surface: oklch(0.215 0.012 248.4);
--color-text: oklch(0.949 0.015 251.2);
--color-text-muted: oklch(0.800 0.018 248.1);
--color-primary: oklch(0.793 0.113 244.7);
--color-primary-foreground: oklch(0.159 0.005 264.4);
--color-accent: oklch(0.820 0.119 150.2);
--color-border: oklch(0.510 0.017 251.3);
--color-focus-ring: oklch(0.793 0.113 244.7);
--color-success: oklch(0.839 0.130 164.9);
--color-success-solid: oklch(0.759 0.143 165.0);
--color-success-solid-foreground: oklch(0.171 0.004 229.1);
--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.171 0.004 229.1);
--color-error: oklch(0.700 0.171 27.0);
--color-error-solid: oklch(0.732 0.165 27.5);
--color-error-solid-foreground: oklch(0.171 0.004 229.1);
--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.171 0.004 229.1);
}
}Tailwind CSS v4 @theme
@theme {
/* light */
--color-background: oklch(0.976 0.012 247.9); /* #f1f8ff */
--color-surface: oklch(0.995 0.007 197.0); /* #f8ffff */
--color-text: oklch(0.220 0.014 253.1); /* #161b21 */
--color-text-muted: oklch(0.460 0.018 251.3); /* #515962 */
--color-primary: oklch(0.501 0.130 249.9); /* #1666aa */
--color-primary-foreground: oklch(0.990 0.005 247.9); /* #f9fcff */
--color-accent: oklch(0.5212 0.1205 150.12); /* #287c42 */
--color-border: oklch(0.646 0.018 250.9); /* #868f99 */
--color-focus-ring: oklch(0.451 0.130 250.6); /* #00579a */
--color-success: oklch(0.5333 0.1163 162.37); /* #008158 */
--color-success-solid: oklch(0.5415 0.1211 160.71); /* #008457 */
--color-success-solid-foreground: oklch(0.990 0.005 247.9); /* #f9fcff */
--color-warning: oklch(0.555 0.122 67.9); /* #a16300 */
--color-warning-solid: oklch(0.8335 0.1508 76.88); /* #ffba44 */
--color-warning-solid-foreground: oklch(0.171 0.004 229.1); /* #0e1011 */
--color-error: oklch(0.4603 0.1706 26.95); /* #a21a1b */
--color-error-solid: oklch(0.551 0.187 27.1); /* #c8322e */
--color-error-solid-foreground: oklch(0.990 0.005 247.9); /* #f9fcff */
--color-info: oklch(0.540 0.120 245.0); /* #2274af */
--color-info-solid: oklch(0.5501 0.1324 245.13); /* #1377b9 */
--color-info-solid-foreground: oklch(0.990 0.005 247.9); /* #f9fcff */
/* dark: use as dark:bg-background-dark, or remap under a .dark selector */
--color-background-dark: oklch(0.170 0.013 248.6); /* #0b1015 */
--color-surface-dark: oklch(0.215 0.012 248.4); /* #151a1f */
--color-text-dark: oklch(0.949 0.015 251.2); /* #e7eff8 */
--color-text-muted-dark: oklch(0.800 0.018 248.1); /* #b5bfc9 */
--color-primary-dark: oklch(0.793 0.113 244.7); /* #7ac3ff */
--color-primary-foreground-dark: oklch(0.159 0.005 264.4); /* #0c0d0f */
--color-accent-dark: oklch(0.820 0.119 150.2); /* #89da9b */
--color-border-dark: oklch(0.510 0.017 251.3); /* #5f6770 */
--color-focus-ring-dark: oklch(0.793 0.113 244.7); /* #7ac3ff */
--color-success-dark: oklch(0.839 0.130 164.9); /* #6ce5b5 */
--color-success-solid-dark: oklch(0.759 0.143 165.0); /* #3bcd9a */
--color-success-solid-foreground-dark: oklch(0.171 0.004 229.1); /* #0e1011 */
--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.171 0.004 229.1); /* #0e1011 */
--color-error-dark: oklch(0.700 0.171 27.0); /* #f66d62 */
--color-error-solid-dark: oklch(0.732 0.165 27.5); /* #ff7a6d */
--color-error-solid-foreground-dark: oklch(0.171 0.004 229.1); /* #0e1011 */
--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.171 0.004 229.1); /* #0e1011 */
}shadcn/ui variables (:root and .dark)
:root {
--background: oklch(0.976 0.012 247.9);
--foreground: oklch(0.220 0.014 253.1);
--card: oklch(0.995 0.007 197.0);
--card-foreground: oklch(0.220 0.014 253.1);
--popover: oklch(0.995 0.007 197.0);
--popover-foreground: oklch(0.220 0.014 253.1);
--primary: oklch(0.501 0.130 249.9);
--primary-foreground: oklch(0.990 0.005 247.9);
--secondary: oklch(0.995 0.007 197.0);
--secondary-foreground: oklch(0.220 0.014 253.1);
--muted: oklch(0.995 0.007 197.0);
--muted-foreground: oklch(0.460 0.018 251.3);
--accent: oklch(0.995 0.007 197.0);
--accent-foreground: oklch(0.220 0.014 253.1);
--destructive: oklch(0.551 0.187 27.1);
--destructive-foreground: oklch(0.990 0.005 247.9);
--border: oklch(0.646 0.018 250.9);
--input: oklch(0.646 0.018 250.9);
--ring: oklch(0.451 0.130 250.6);
--success: oklch(0.5415 0.1211 160.71);
--success-foreground: oklch(0.990 0.005 247.9);
--warning: oklch(0.8335 0.1508 76.88);
--warning-foreground: oklch(0.171 0.004 229.1);
--info: oklch(0.5501 0.1324 245.13);
--info-foreground: oklch(0.990 0.005 247.9);
--brand-accent: oklch(0.5212 0.1205 150.12);
}
.dark {
--background: oklch(0.170 0.013 248.6);
--foreground: oklch(0.949 0.015 251.2);
--card: oklch(0.215 0.012 248.4);
--card-foreground: oklch(0.949 0.015 251.2);
--popover: oklch(0.215 0.012 248.4);
--popover-foreground: oklch(0.949 0.015 251.2);
--primary: oklch(0.793 0.113 244.7);
--primary-foreground: oklch(0.159 0.005 264.4);
--secondary: oklch(0.215 0.012 248.4);
--secondary-foreground: oklch(0.949 0.015 251.2);
--muted: oklch(0.215 0.012 248.4);
--muted-foreground: oklch(0.800 0.018 248.1);
--accent: oklch(0.215 0.012 248.4);
--accent-foreground: oklch(0.949 0.015 251.2);
--destructive: oklch(0.732 0.165 27.5);
--destructive-foreground: oklch(0.171 0.004 229.1);
--border: oklch(0.510 0.017 251.3);
--input: oklch(0.510 0.017 251.3);
--ring: oklch(0.793 0.113 244.7);
--success: oklch(0.759 0.143 165.0);
--success-foreground: oklch(0.171 0.004 229.1);
--warning: oklch(0.7605 0.1539 75.30);
--warning-foreground: oklch(0.171 0.004 229.1);
--info: oklch(0.760 0.132 244.8);
--info-foreground: oklch(0.171 0.004 229.1);
--brand-accent: oklch(0.820 0.119 150.2);
}Design tokens (DTCG-style JSON)
{
"color": {
"light": {
"background": {
"$type": "color",
"$value": "oklch(0.976 0.012 247.9)",
"$description": "sRGB #f1f8ff"
},
"surface": {
"$type": "color",
"$value": "oklch(0.995 0.007 197.0)",
"$description": "sRGB #f8ffff"
},
"text": {
"$type": "color",
"$value": "oklch(0.220 0.014 253.1)",
"$description": "sRGB #161b21"
},
"text-muted": {
"$type": "color",
"$value": "oklch(0.460 0.018 251.3)",
"$description": "sRGB #515962"
},
"primary": {
"$type": "color",
"$value": "oklch(0.501 0.130 249.9)",
"$description": "sRGB #1666aa"
},
"primary-foreground": {
"$type": "color",
"$value": "oklch(0.990 0.005 247.9)",
"$description": "sRGB #f9fcff"
},
"accent": {
"$type": "color",
"$value": "oklch(0.5212 0.1205 150.12)",
"$description": "sRGB #287c42"
},
"border": {
"$type": "color",
"$value": "oklch(0.646 0.018 250.9)",
"$description": "sRGB #868f99"
},
"focus-ring": {
"$type": "color",
"$value": "oklch(0.451 0.130 250.6)",
"$description": "sRGB #00579a"
},
"success": {
"$type": "color",
"$value": "oklch(0.5333 0.1163 162.37)",
"$description": "sRGB #008158"
},
"success-solid": {
"$type": "color",
"$value": "oklch(0.5415 0.1211 160.71)",
"$description": "sRGB #008457"
},
"success-solid-foreground": {
"$type": "color",
"$value": "oklch(0.990 0.005 247.9)",
"$description": "sRGB #f9fcff"
},
"warning": {
"$type": "color",
"$value": "oklch(0.555 0.122 67.9)",
"$description": "sRGB #a16300"
},
"warning-solid": {
"$type": "color",
"$value": "oklch(0.8335 0.1508 76.88)",
"$description": "sRGB #ffba44"
},
"warning-solid-foreground": {
"$type": "color",
"$value": "oklch(0.171 0.004 229.1)",
"$description": "sRGB #0e1011"
},
"error": {
"$type": "color",
"$value": "oklch(0.4603 0.1706 26.95)",
"$description": "sRGB #a21a1b"
},
"error-solid": {
"$type": "color",
"$value": "oklch(0.551 0.187 27.1)",
"$description": "sRGB #c8322e"
},
"error-solid-foreground": {
"$type": "color",
"$value": "oklch(0.990 0.005 247.9)",
"$description": "sRGB #f9fcff"
},
"info": {
"$type": "color",
"$value": "oklch(0.540 0.120 245.0)",
"$description": "sRGB #2274af"
},
"info-solid": {
"$type": "color",
"$value": "oklch(0.5501 0.1324 245.13)",
"$description": "sRGB #1377b9"
},
"info-solid-foreground": {
"$type": "color",
"$value": "oklch(0.990 0.005 247.9)",
"$description": "sRGB #f9fcff"
}
},
"dark": {
"background": {
"$type": "color",
"$value": "oklch(0.170 0.013 248.6)",
"$description": "sRGB #0b1015"
},
"surface": {
"$type": "color",
"$value": "oklch(0.215 0.012 248.4)",
"$description": "sRGB #151a1f"
},
"text": {
"$type": "color",
"$value": "oklch(0.949 0.015 251.2)",
"$description": "sRGB #e7eff8"
},
"text-muted": {
"$type": "color",
"$value": "oklch(0.800 0.018 248.1)",
"$description": "sRGB #b5bfc9"
},
"primary": {
"$type": "color",
"$value": "oklch(0.793 0.113 244.7)",
"$description": "sRGB #7ac3ff"
},
"primary-foreground": {
"$type": "color",
"$value": "oklch(0.159 0.005 264.4)",
"$description": "sRGB #0c0d0f"
},
"accent": {
"$type": "color",
"$value": "oklch(0.820 0.119 150.2)",
"$description": "sRGB #89da9b"
},
"border": {
"$type": "color",
"$value": "oklch(0.510 0.017 251.3)",
"$description": "sRGB #5f6770"
},
"focus-ring": {
"$type": "color",
"$value": "oklch(0.793 0.113 244.7)",
"$description": "sRGB #7ac3ff"
},
"success": {
"$type": "color",
"$value": "oklch(0.839 0.130 164.9)",
"$description": "sRGB #6ce5b5"
},
"success-solid": {
"$type": "color",
"$value": "oklch(0.759 0.143 165.0)",
"$description": "sRGB #3bcd9a"
},
"success-solid-foreground": {
"$type": "color",
"$value": "oklch(0.171 0.004 229.1)",
"$description": "sRGB #0e1011"
},
"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.171 0.004 229.1)",
"$description": "sRGB #0e1011"
},
"error": {
"$type": "color",
"$value": "oklch(0.700 0.171 27.0)",
"$description": "sRGB #f66d62"
},
"error-solid": {
"$type": "color",
"$value": "oklch(0.732 0.165 27.5)",
"$description": "sRGB #ff7a6d"
},
"error-solid-foreground": {
"$type": "color",
"$value": "oklch(0.171 0.004 229.1)",
"$description": "sRGB #0e1011"
},
"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.171 0.004 229.1)",
"$description": "sRGB #0e1011"
}
}
}
}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 #1666AA in the OKLCH picker →Build a tonal scale →Check a pair
FAQ
- Is dark mode better for accessibility?
- It depends on the person. Some with light sensitivity prefer it, while some people with astigmatism find light-on-dark text harder to read. That is why offering a choice is better than choosing for everyone.
- Why is Lc negative in the table?
- APCA reports light text on a dark background as a negative Lc and dark on light as positive. Magnitude is what you compare to the published tiers.
- Can I export only the dark values?
- The exports contain both themes. Use the .dark block or the -dark Tailwind tokens for the dark values.
- How do I convert my existing light palette?
- Keep each role's hue and chroma, choose a lightness that passes against the dark surface, and verify with the contrast checker. That is the method this page uses.