OKLCH Studio · Accessible palettes · Product & UI
Accessible color palette for mobile apps (WCAG 2.2 verified)
Updated 2026-10-19
Mobile apps are used in the worst viewing conditions of any product: in sunlight, on the move, with a cracked screen, a battery-saving brightness setting or one hand. The screen is small, so a single faint label or disabled-looking button is a big proportion of the interface. Contrast that passes in an office can be lost on a train platform.
Phones also bring system features that change colour: dark mode, increased contrast, colour filters and larger text. A good palette treats dark mode as a first-class theme rather than an inversion, and this one verifies both. WCAG 2.2 AA is the usual baseline for apps, and the platform guidelines from Apple and Google point to similar contrast levels.
Touch targets and gestures mean colour often does the work of affordance: a tinted tab, a filled floating button. Each of those roles is covered here, with borders and focus rings at 3:1 for switch and keyboard users.
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
#FAFAFA
oklch(0.985 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
#585858
oklch(0.460 0.000 0.0)
Sample text
- Primary
#5D4DBE
oklch(0.499 0.170 284.9)
Sample text
- Primary foreground
#FCFCFC
oklch(0.991 0.000 0.0)
Sample text
- Accent
#007F4E
oklch(0.525 0.123 157.9)
Sample text
- Border
#909090
oklch(0.653 0.000 0.0)
Sample text
- Focus ring
#503EAD
oklch(0.450 0.169 284.7)
Sample text
- Success
#00835A
oklch(0.539 0.117 162.7)
Sample text
- Success fill
#008558
oklch(0.545 0.122 160.9)
Sample text
- Success fill label
#FCFCFC
oklch(0.991 0.000 0.0)
Sample text
- Warning
#A36500
oklch(0.562 0.123 68.5)
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
#C8322E
oklch(0.551 0.187 27.1)
Sample text
- Error fill label
#FCFCFC
oklch(0.991 0.000 0.0)
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
#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
#161616
oklch(0.200 0.000 0.0)
Sample text
- Surface
#202020
oklch(0.244 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
#B4ABFF
oklch(0.780 0.119 288.2)
Sample text
- Primary foreground
#0D0D0D
oklch(0.159 0.000 0.0)
Sample text
- Accent
#6FDEA7
oklch(0.820 0.130 160.0)
Sample text
- Border
#6B6B6B
oklch(0.528 0.000 0.0)
Sample text
- Focus ring
#B4ABFF
oklch(0.780 0.119 288.2)
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
#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
#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
#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 #fafafa | 16.50:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +101.1 | 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#585858 on #fafafa | 6.81:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +81.7 | Lc 75: body text | Open in checker |
| Muted text on surface#585858 on #fefefe | 7.05:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +84.1 | Lc 75: body text | Open in checker |
| Primary as link text on background#5d4dbe on #fafafa | 6.15:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +78.5 | Lc 75: body text | Open in checker |
| Primary as link text on surface#5d4dbe on #fefefe | 6.36:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +80.9 | Lc 75: body text | Open in checker |
| Button label on primary#fcfcfc on #5d4dbe | 6.25:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −84.6 | Lc 75: body text | Open in checker |
| Accent text on background#007f4e on #fafafa | 4.85:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +71.4 | Lc 60: larger content text | Open in checker |
| Accent text on surface#007f4e on #fefefe | 5.02:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +73.8 | Lc 60: larger content text | Open in checker |
| Border on background#909090 on #fafafa | 3.05:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | +56.1 | Lc 45: headlines and large text | Open in checker |
| Border on surface#909090 on #fefefe | 3.16:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | +58.5 | Lc 45: headlines and large text | Open in checker |
| Focus ring on background#503ead on #fafafa | 7.63:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | +84.2 | Lc 75: body text | Open in checker |
| Focus ring on surface#503ead on #fefefe | 7.90:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | +86.6 | Lc 75: body text | Open in checker |
| Success text on background#00835a on #fafafa | 4.57:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +69.6 | Lc 60: larger content text | Open in checker |
| Success text on surface#00835a on #fefefe | 4.73:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +72.0 | Lc 60: larger content text | Open in checker |
| Success badge label on fill#fcfcfc on #008558 | 4.55:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −75.3 | Lc 75: body text | Open in checker |
| Warning text on background#a36500 on #fafafa | 4.55:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +69.6 | Lc 60: larger content text | Open in checker |
| Warning text on surface#a36500 on #fefefe | 4.71:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +72.0 | Lc 60: larger content text | Open in checker |
| Warning badge label on fill#0f0f0f on #ffba44 | 11.28:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +73.2 | Lc 60: larger content text | Open in checker |
| Error text on background#a21a1b on #fafafa | 7.45:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +82.3 | Lc 75: body text | Open in checker |
| Error text on surface#a21a1b on #fefefe | 7.71:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +84.7 | Lc 75: body text | Open in checker |
| Error badge label on fill#fcfcfc on #c8322e | 5.17:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −78.4 | Lc 75: body text | Open in checker |
| Info text on background#2274af on #fafafa | 4.80:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +71.3 | Lc 60: larger content text | Open in checker |
| Info text on surface#2274af on #fefefe | 4.97:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +73.7 | Lc 60: larger content text | Open in checker |
| Info badge label on fill#fcfcfc on #1377b9 | 4.68:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −76.3 | 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 #161616 | 15.59:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −95.8 | Lc 75: body text | Open in checker |
| Text on surface#eeeeee on #202020 | 14.04:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −94.6 | Lc 75: body text | Open in checker |
| Muted text on background#bebebe on #161616 | 9.73:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −66.5 | Lc 60: larger content text | Open in checker |
| Muted text on surface#bebebe on #202020 | 8.76:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −65.3 | Lc 60: larger content text | Open in checker |
| Primary as link text on background#b4abff on #161616 | 8.79:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −61.3 | Lc 60: larger content text | Open in checker |
| Primary as link text on surface#b4abff on #202020 | 7.92:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −60.1 | Lc 60: larger content text | Open in checker |
| Button label on primary#0d0d0d on #b4abff | 9.44:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +63.5 | Lc 60: larger content text | Open in checker |
| Accent text on background#6fdea7 on #161616 | 10.92:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −73.2 | Lc 60: larger content text | Open in checker |
| Accent text on surface#6fdea7 on #202020 | 9.83:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −72.0 | Lc 60: larger content text | Open in checker |
| Border on background#6b6b6b on #161616 | 3.39:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | −24.3 | Below Lc 30 | Open in checker |
| Border on surface#6b6b6b on #202020 | 3.05:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | −23.1 | Below Lc 30 | Open in checker |
| Focus ring on background#b4abff on #161616 | 8.79:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | −61.3 | Lc 60: larger content text | Open in checker |
| Focus ring on surface#b4abff on #202020 | 7.92:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | −60.1 | Lc 60: larger content text | Open in checker |
| Success text on background#6ce5b5 on #161616 | 11.64:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −77.0 | Lc 75: body text | Open in checker |
| Success text on surface#6ce5b5 on #202020 | 10.48:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −75.8 | Lc 75: body text | Open in checker |
| Success badge label on fill#0f0f0f on #3bcd9a | 9.47:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +64.6 | Lc 60: larger content text | Open in checker |
| Warning text on background#ffbc56 on #161616 | 10.84:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −72.7 | Lc 60: larger content text | Open in checker |
| Warning text on surface#ffbc56 on #202020 | 9.76:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −71.5 | Lc 60: larger content text | Open in checker |
| Warning badge label on fill#0f0f0f on #e9a11d | 8.74: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 #161616 | 6.27:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −46.5 | Lc 45: headlines and large text | Open in checker |
| Error text on surface#f66d62 on #202020 | 5.65:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −45.3 | Lc 45: headlines and large text | Open in checker |
| Error badge label on fill#0f0f0f on #ff7a6d | 7.53: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 #161616 | 9.13:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −63.4 | Lc 60: larger content text | Open in checker |
| Info text on surface#71bfff on #202020 | 8.22:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −62.2 | Lc 60: larger content text | Open in checker |
| Info badge label on fill#0f0f0f on #61b9ff | 9.02:1 | AA 4.5: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 | 34.2 | 26.0 | 39.0 | 31.7 | Shown only |
| success / error | 28.5 | 16.6 | 6.5 | 33.0 | Yes |
| success / info | 17.1 | 16.2 | 15.2 | 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
| 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
Why these colours
The primary is a violet at OKLCH hue 285 and chroma 0.17, high for a serious product but common in consumer apps because it stays recognisable at small icon sizes. The accent is a green at hue 160 for positive confirmations and progress. Violet and green sit far apart on the wheel and differ in lightness once solved.
The neutrals are true greys, chroma zero. On OLED phones, a pure neutral dark theme avoids colour casts, and the dark background is a dark grey rather than black, which reduces the smearing that very bright text on pure black can cause when scrolling. All roles were solved against both background and card colours.
Brightness behaviour is worth testing on real hardware. Automatic brightness can drop the screen to a dim level indoors, and an outdoor boost can wash out low-contrast colours. A short test routine, with the app open at minimum brightness and then in direct light, reveals problems that numbers alone can miss, and the margin in these pairs helps in both conditions.
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 badge label on fill in the light theme at 4.55: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
- Treat the dark theme as the default when the system asks for it, and test each screen in both themes.
- Do not signal state with colour alone on tab bars; use a label, an icon change or an underline.
- Keep tap targets at least 44 by 44 points or 48 dp, and use the border colour to outline secondary buttons.
- Use the success badge and the info badge for toasts and banners, with text, and keep them on screen long enough to read.
- Support the system's larger text and bold text settings; contrast and size work together.
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: #fafafa;
--color-surface: #fefefe;
--color-text: #1b1b1b;
--color-text-muted: #585858;
--color-primary: #5d4dbe;
--color-primary-foreground: #fcfcfc;
--color-accent: #007f4e;
--color-border: #909090;
--color-focus-ring: #503ead;
--color-success: #00835a;
--color-success-solid: #008558;
--color-success-solid-foreground: #fcfcfc;
--color-warning: #a36500;
--color-warning-solid: #ffba44;
--color-warning-solid-foreground: #0f0f0f;
--color-error: #a21a1b;
--color-error-solid: #c8322e;
--color-error-solid-foreground: #fcfcfc;
--color-info: #2274af;
--color-info-solid: #1377b9;
--color-info-solid-foreground: #fcfcfc;
}
.dark {
--color-background: #161616;
--color-surface: #202020;
--color-text: #eeeeee;
--color-text-muted: #bebebe;
--color-primary: #b4abff;
--color-primary-foreground: #0d0d0d;
--color-accent: #6fdea7;
--color-border: #6b6b6b;
--color-focus-ring: #b4abff;
--color-success: #6ce5b5;
--color-success-solid: #3bcd9a;
--color-success-solid-foreground: #0f0f0f;
--color-warning: #ffbc56;
--color-warning-solid: #e9a11d;
--color-warning-solid-foreground: #0f0f0f;
--color-error: #f66d62;
--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.985 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.460 0.000 0.0);
--color-primary: oklch(0.499 0.170 284.9);
--color-primary-foreground: oklch(0.991 0.000 0.0);
--color-accent: oklch(0.525 0.123 157.9);
--color-border: oklch(0.653 0.000 0.0);
--color-focus-ring: oklch(0.450 0.169 284.7);
--color-success: oklch(0.539 0.117 162.7);
--color-success-solid: oklch(0.545 0.122 160.9);
--color-success-solid-foreground: oklch(0.991 0.000 0.0);
--color-warning: oklch(0.562 0.123 68.5);
--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.551 0.187 27.1);
--color-error-solid-foreground: oklch(0.991 0.000 0.0);
--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.991 0.000 0.0);
}
.dark {
--color-background: oklch(0.200 0.000 0.0);
--color-surface: oklch(0.244 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.780 0.119 288.2);
--color-primary-foreground: oklch(0.159 0.000 0.0);
--color-accent: oklch(0.820 0.130 160.0);
--color-border: oklch(0.528 0.000 0.0);
--color-focus-ring: oklch(0.780 0.119 288.2);
--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.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.700 0.171 27.0);
--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.985 0.000 0.0); /* #fafafa */
--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.460 0.000 0.0); /* #585858 */
--color-primary: oklch(0.499 0.170 284.9); /* #5d4dbe */
--color-primary-foreground: oklch(0.991 0.000 0.0); /* #fcfcfc */
--color-accent: oklch(0.525 0.123 157.9); /* #007f4e */
--color-border: oklch(0.653 0.000 0.0); /* #909090 */
--color-focus-ring: oklch(0.450 0.169 284.7); /* #503ead */
--color-success: oklch(0.539 0.117 162.7); /* #00835a */
--color-success-solid: oklch(0.545 0.122 160.9); /* #008558 */
--color-success-solid-foreground: oklch(0.991 0.000 0.0); /* #fcfcfc */
--color-warning: oklch(0.562 0.123 68.5); /* #a36500 */
--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.551 0.187 27.1); /* #c8322e */
--color-error-solid-foreground: oklch(0.991 0.000 0.0); /* #fcfcfc */
--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.991 0.000 0.0); /* #fcfcfc */
/* dark: use as dark:bg-background-dark, or remap under a .dark selector */
--color-background-dark: oklch(0.200 0.000 0.0); /* #161616 */
--color-surface-dark: oklch(0.244 0.000 0.0); /* #202020 */
--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.780 0.119 288.2); /* #b4abff */
--color-primary-foreground-dark: oklch(0.159 0.000 0.0); /* #0d0d0d */
--color-accent-dark: oklch(0.820 0.130 160.0); /* #6fdea7 */
--color-border-dark: oklch(0.528 0.000 0.0); /* #6b6b6b */
--color-focus-ring-dark: oklch(0.780 0.119 288.2); /* #b4abff */
--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.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.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.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.985 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.499 0.170 284.9);
--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.460 0.000 0.0);
--accent: oklch(0.997 0.000 0.0);
--accent-foreground: oklch(0.222 0.000 0.0);
--destructive: oklch(0.551 0.187 27.1);
--destructive-foreground: oklch(0.991 0.000 0.0);
--border: oklch(0.653 0.000 0.0);
--input: oklch(0.653 0.000 0.0);
--ring: oklch(0.450 0.169 284.7);
--success: oklch(0.545 0.122 160.9);
--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.5501 0.1324 245.13);
--info-foreground: oklch(0.991 0.000 0.0);
--brand-accent: oklch(0.525 0.123 157.9);
}
.dark {
--background: oklch(0.200 0.000 0.0);
--foreground: oklch(0.949 0.000 0.0);
--card: oklch(0.244 0.000 0.0);
--card-foreground: oklch(0.949 0.000 0.0);
--popover: oklch(0.244 0.000 0.0);
--popover-foreground: oklch(0.949 0.000 0.0);
--primary: oklch(0.780 0.119 288.2);
--primary-foreground: oklch(0.159 0.000 0.0);
--secondary: oklch(0.244 0.000 0.0);
--secondary-foreground: oklch(0.949 0.000 0.0);
--muted: oklch(0.244 0.000 0.0);
--muted-foreground: oklch(0.802 0.000 0.0);
--accent: oklch(0.244 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.528 0.000 0.0);
--input: oklch(0.528 0.000 0.0);
--ring: oklch(0.780 0.119 288.2);
--success: oklch(0.759 0.143 165.0);
--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.820 0.130 160.0);
}Design tokens (DTCG-style JSON)
{
"color": {
"light": {
"background": {
"$type": "color",
"$value": "oklch(0.985 0.000 0.0)",
"$description": "sRGB #fafafa"
},
"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.460 0.000 0.0)",
"$description": "sRGB #585858"
},
"primary": {
"$type": "color",
"$value": "oklch(0.499 0.170 284.9)",
"$description": "sRGB #5d4dbe"
},
"primary-foreground": {
"$type": "color",
"$value": "oklch(0.991 0.000 0.0)",
"$description": "sRGB #fcfcfc"
},
"accent": {
"$type": "color",
"$value": "oklch(0.525 0.123 157.9)",
"$description": "sRGB #007f4e"
},
"border": {
"$type": "color",
"$value": "oklch(0.653 0.000 0.0)",
"$description": "sRGB #909090"
},
"focus-ring": {
"$type": "color",
"$value": "oklch(0.450 0.169 284.7)",
"$description": "sRGB #503ead"
},
"success": {
"$type": "color",
"$value": "oklch(0.539 0.117 162.7)",
"$description": "sRGB #00835a"
},
"success-solid": {
"$type": "color",
"$value": "oklch(0.545 0.122 160.9)",
"$description": "sRGB #008558"
},
"success-solid-foreground": {
"$type": "color",
"$value": "oklch(0.991 0.000 0.0)",
"$description": "sRGB #fcfcfc"
},
"warning": {
"$type": "color",
"$value": "oklch(0.562 0.123 68.5)",
"$description": "sRGB #a36500"
},
"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.551 0.187 27.1)",
"$description": "sRGB #c8322e"
},
"error-solid-foreground": {
"$type": "color",
"$value": "oklch(0.991 0.000 0.0)",
"$description": "sRGB #fcfcfc"
},
"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.991 0.000 0.0)",
"$description": "sRGB #fcfcfc"
}
},
"dark": {
"background": {
"$type": "color",
"$value": "oklch(0.200 0.000 0.0)",
"$description": "sRGB #161616"
},
"surface": {
"$type": "color",
"$value": "oklch(0.244 0.000 0.0)",
"$description": "sRGB #202020"
},
"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.780 0.119 288.2)",
"$description": "sRGB #b4abff"
},
"primary-foreground": {
"$type": "color",
"$value": "oklch(0.159 0.000 0.0)",
"$description": "sRGB #0d0d0d"
},
"accent": {
"$type": "color",
"$value": "oklch(0.820 0.130 160.0)",
"$description": "sRGB #6fdea7"
},
"border": {
"$type": "color",
"$value": "oklch(0.528 0.000 0.0)",
"$description": "sRGB #6b6b6b"
},
"focus-ring": {
"$type": "color",
"$value": "oklch(0.780 0.119 288.2)",
"$description": "sRGB #b4abff"
},
"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.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.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.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 #5D4DBE in the OKLCH picker →Build a tonal scale →Check a pair
FAQ
- Is a dark grey better than black?
- Many designers prefer a dark grey because very bright text on pure black can look harsh. Both can be accessible; here the dark background is verified at 3:1 or better for every role.
- Does this satisfy iOS or Android guidelines?
- The ratios meet common platform guidance for text contrast, but check the current platform guidelines yourself, because they change and cover more than colour.
- Can I export to CSS only?
- Yes. The CSS block works for web views and PWAs; for native apps, take the hex values from the swatch grid.
- What about increased contrast mode?
- Consider supporting it by moving to the high-contrast palette values when the system requests it.