OKLCH Studio · Accessible palettes · Product & UI
Accessible color palette for design systems (WCAG 2.2 verified)
Updated 2026-10-19
A design system's colour tokens are a promise to every team that uses them: pick this text token on that surface token and the result is readable. If the promise is broken in one pair, every product built on the system inherits the problem. Verifying pairs once, in the token set, is far cheaper than verifying them in every screen.
This palette is structured as a semantic set. There are surfaces (background, surface), content (text, muted text), brand (primary and its foreground, accent), structure (border, focus ring) and four status roles, each with a text form and a filled form with a label colour. Every intended pairing is verified in light and dark themes.
WCAG 2.2 AA is the standard most design-system accessibility policies adopt, and body and muted text here are targeted at AAA. The exports cover plain CSS custom properties, a Tailwind v4 theme block, shadcn/ui variables and design-token JSON.
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
#F5F7F9
oklch(0.975 0.003 247.9)
Sample text
- Surface
#FDFEFF
oklch(0.997 0.002 247.8)
Sample text
- Text
#191B1D
oklch(0.221 0.005 248.1)
Sample text
- Muted text
#525458
oklch(0.445 0.007 264.5)
Sample text
- Primary
#2C5DBD
oklch(0.500 0.161 262.0)
Sample text
- Primary foreground
#FBFCFD
oklch(0.991 0.002 247.8)
Sample text
- Accent
#974391
oklch(0.521 0.149 330.3)
Sample text
- Border
#8C8E92
oklch(0.646 0.006 264.5)
Sample text
- Focus ring
#1E4EAC
oklch(0.450 0.160 261.9)
Sample text
- Success
#008158
oklch(0.5333 0.1163 162.37)
Sample text
- Success fill
#008557
oklch(0.5443 0.1224 160.36)
Sample text
- Success fill label
#FBFCFD
oklch(0.991 0.002 247.8)
Sample text
- Warning
#A06300
oklch(0.5541 0.1212 68.45)
Sample text
- Warning fill
#FFBA44
oklch(0.8335 0.1508 76.88)
Sample text
- Warning fill label
#0F0F10
oklch(0.169 0.002 286.2)
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
#FBFCFD
oklch(0.991 0.002 247.8)
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
#FBFCFD
oklch(0.991 0.002 247.8)
Sample text
Appointment confirmed
Secondary text sits on the surface colour.
Link in primary · Accent text
Dark theme
- Background
#151618
oklch(0.200 0.004 264.5)
Sample text
- Surface
#1F2022
oklch(0.243 0.004 264.5)
Sample text
- Text
#EDEEF2
oklch(0.949 0.005 275.0)
Sample text
- Muted text
#BBBEC2
oklch(0.800 0.007 255.5)
Sample text
- Primary
#84BCFF
oklch(0.783 0.113 253.5)
Sample text
- Primary foreground
#0D0D0E
oklch(0.160 0.002 286.2)
Sample text
- Accent
#FA9FF2
oklch(0.820 0.149 330.0)
Sample text
- Border
#696B6E
oklch(0.527 0.005 258.3)
Sample text
- Focus ring
#84BCFF
oklch(0.783 0.113 253.5)
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
#0F0F10
oklch(0.169 0.002 286.2)
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
#0F0F10
oklch(0.169 0.002 286.2)
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
#0F0F10
oklch(0.169 0.002 286.2)
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
#0F0F10
oklch(0.169 0.002 286.2)
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#191b1d on #f5f7f9 | 16.08:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +99.2 | Lc 75: body text | Open in checker |
| Text on surface#191b1d on #fdfeff | 17.10:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +103.5 | Lc 75: body text | Open in checker |
| Muted text on background#525458 on #f5f7f9 | 7.06:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +81.4 | Lc 75: body text | Open in checker |
| Muted text on surface#525458 on #fdfeff | 7.51:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +85.7 | Lc 75: body text | Open in checker |
| Primary as link text on background#2c5dbd on #f5f7f9 | 5.73:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +75.3 | Lc 75: body text | Open in checker |
| Primary as link text on surface#2c5dbd on #fdfeff | 6.10:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +79.6 | Lc 75: body text | Open in checker |
| Button label on primary#fbfcfd on #2c5dbd | 5.99:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −83.4 | Lc 75: body text | Open in checker |
| Accent text on background#974391 on #f5f7f9 | 5.54:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +74.3 | Lc 60: larger content text | Open in checker |
| Accent text on surface#974391 on #fdfeff | 5.89:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +78.6 | Lc 75: body text | Open in checker |
| Border on background#8c8e92 on #f5f7f9 | 3.05:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | +55.2 | Lc 45: headlines and large text | Open in checker |
| Border on surface#8c8e92 on #fdfeff | 3.24:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | +59.5 | Lc 45: headlines and large text | Open in checker |
| Focus ring on background#1e4eac on #f5f7f9 | 7.13:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | +81.1 | Lc 75: body text | Open in checker |
| Focus ring on surface#1e4eac on #fdfeff | 7.58:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | +85.4 | Lc 75: body text | Open in checker |
| Success text on background#008158 on #f5f7f9 | 4.56:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +68.4 | Lc 60: larger content text | Open in checker |
| Success text on surface#008158 on #fdfeff | 4.85:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +72.7 | Lc 60: larger content text | Open in checker |
| Success badge label on fill#fbfcfd on #008557 | 4.55:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −75.2 | Lc 75: body text | Open in checker |
| Warning text on background#a06300 on #f5f7f9 | 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#a06300 on #fdfeff | 4.85:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +72.9 | Lc 60: larger content text | Open in checker |
| Warning badge label on fill#0f0f10 on #ffba44 | 11.27: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 #f5f7f9 | 7.24:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +80.3 | Lc 75: body text | Open in checker |
| Error text on surface#a21a1b on #fdfeff | 7.70:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +84.6 | Lc 75: body text | Open in checker |
| Error badge label on fill#fbfcfd on #c8322e | 5.17:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −78.3 | Lc 75: body text | Open in checker |
| Info text on background#2274af on #f5f7f9 | 4.67:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +69.3 | Lc 60: larger content text | Open in checker |
| Info text on surface#2274af on #fdfeff | 4.96:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +73.6 | Lc 60: larger content text | Open in checker |
| Info badge label on fill#fbfcfd on #1377b9 | 4.68:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −76.2 | 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#edeef2 on #151618 | 15.61:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −95.9 | Lc 75: body text | Open in checker |
| Text on surface#edeef2 on #1f2022 | 14.06:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −94.7 | Lc 75: body text | Open in checker |
| Muted text on background#bbbec2 on #151618 | 9.70:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −66.4 | Lc 60: larger content text | Open in checker |
| Muted text on surface#bbbec2 on #1f2022 | 8.74:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −65.2 | Lc 60: larger content text | Open in checker |
| Primary as link text on background#84bcff on #151618 | 9.15:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −63.4 | Lc 60: larger content text | Open in checker |
| Primary as link text on surface#84bcff on #1f2022 | 8.24:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −62.2 | Lc 60: larger content text | Open in checker |
| Button label on primary#0d0d0e on #84bcff | 9.82:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +65.5 | Lc 60: larger content text | Open in checker |
| Accent text on background#fa9ff2 on #151618 | 9.74:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −66.8 | Lc 60: larger content text | Open in checker |
| Accent text on surface#fa9ff2 on #1f2022 | 8.77:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −65.6 | Lc 60: larger content text | Open in checker |
| Border on background#696b6e on #151618 | 3.38:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | −24.2 | Below Lc 30 | Open in checker |
| Border on surface#696b6e on #1f2022 | 3.05:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | −23.0 | Below Lc 30 | Open in checker |
| Focus ring on background#84bcff on #151618 | 9.15:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | −63.4 | Lc 60: larger content text | Open in checker |
| Focus ring on surface#84bcff on #1f2022 | 8.24:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | −62.2 | Lc 60: larger content text | Open in checker |
| Success text on background#6ce5b5 on #151618 | 11.65:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −77.0 | Lc 75: body text | Open in checker |
| Success text on surface#6ce5b5 on #1f2022 | 10.49:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −75.8 | Lc 75: body text | Open in checker |
| Success badge label on fill#0f0f10 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 #151618 | 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 #1f2022 | 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#0f0f10 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 #151618 | 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 #1f2022 | 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#0f0f10 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 #151618 | 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 #1f2022 | 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#0f0f10 on #61b9ff | 9.02:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +62.0 | 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 | 38.9 | 31.8 | Shown only |
| success / error | 28.6 | 16.6 | 6.3 | 33.0 | Yes |
| success / info | 17.2 | 16.3 | 15.4 | 3.3 | 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 blue at OKLCH hue 262 with chroma 0.16 and the accent a magenta at hue 330. Blue is the most common neutral-feeling brand colour for systems, and magenta is the farthest from it that still keeps clear of the status hues. Neutrals carry only a trace of tint at 0.004, so they behave almost like greys.
Both text and muted text are AAA, a choice that costs little in a system because muted text is typically the lightest-looking neutral and tends to fail first. With muted text at 7:1, teams can use it without checking. Everything else is AA, with borders and focus rings at 3:1 as WCAG 1.4.11 requires.
The generator solved 50 pairs (25 light, 25 dark), and all of them meet their target on the raw WCAG ratio. The tightest is muted text on background in the light theme at 7.06: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
- Document which tokens may be paired; the table here is a starting list, and any pair outside it should be checked.
- Name tokens by role, such as text-muted, not by appearance, such as grey-600, so the dark theme can remap them.
- Add a lint or test that fails the build if a token pair drops below its target; the generator here throws in the same situation.
- Do not use the accent for text on tinted surfaces that are not in the table.
- Version your tokens and note contrast changes in the changelog, because a small lightness shift can break a downstream pair.
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: #f5f7f9;
--color-surface: #fdfeff;
--color-text: #191b1d;
--color-text-muted: #525458;
--color-primary: #2c5dbd;
--color-primary-foreground: #fbfcfd;
--color-accent: #974391;
--color-border: #8c8e92;
--color-focus-ring: #1e4eac;
--color-success: #008158;
--color-success-solid: #008557;
--color-success-solid-foreground: #fbfcfd;
--color-warning: #a06300;
--color-warning-solid: #ffba44;
--color-warning-solid-foreground: #0f0f10;
--color-error: #a21a1b;
--color-error-solid: #c8322e;
--color-error-solid-foreground: #fbfcfd;
--color-info: #2274af;
--color-info-solid: #1377b9;
--color-info-solid-foreground: #fbfcfd;
}
.dark {
--color-background: #151618;
--color-surface: #1f2022;
--color-text: #edeef2;
--color-text-muted: #bbbec2;
--color-primary: #84bcff;
--color-primary-foreground: #0d0d0e;
--color-accent: #fa9ff2;
--color-border: #696b6e;
--color-focus-ring: #84bcff;
--color-success: #6ce5b5;
--color-success-solid: #3bcd9a;
--color-success-solid-foreground: #0f0f10;
--color-warning: #ffbc56;
--color-warning-solid: #e9a11d;
--color-warning-solid-foreground: #0f0f10;
--color-error: #f66d62;
--color-error-solid: #ff7a6d;
--color-error-solid-foreground: #0f0f10;
--color-info: #71bfff;
--color-info-solid: #61b9ff;
--color-info-solid-foreground: #0f0f10;
}
/* oklch() where supported: the same colours, unclipped values of the hex above. */
@supports (color: oklch(0 0 0)) {
:root {
--color-background: oklch(0.975 0.003 247.9);
--color-surface: oklch(0.997 0.002 247.8);
--color-text: oklch(0.221 0.005 248.1);
--color-text-muted: oklch(0.445 0.007 264.5);
--color-primary: oklch(0.500 0.161 262.0);
--color-primary-foreground: oklch(0.991 0.002 247.8);
--color-accent: oklch(0.521 0.149 330.3);
--color-border: oklch(0.646 0.006 264.5);
--color-focus-ring: oklch(0.450 0.160 261.9);
--color-success: oklch(0.5333 0.1163 162.37);
--color-success-solid: oklch(0.5443 0.1224 160.36);
--color-success-solid-foreground: oklch(0.991 0.002 247.8);
--color-warning: oklch(0.5541 0.1212 68.45);
--color-warning-solid: oklch(0.8335 0.1508 76.88);
--color-warning-solid-foreground: oklch(0.169 0.002 286.2);
--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.002 247.8);
--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.002 247.8);
}
.dark {
--color-background: oklch(0.200 0.004 264.5);
--color-surface: oklch(0.243 0.004 264.5);
--color-text: oklch(0.949 0.005 275.0);
--color-text-muted: oklch(0.800 0.007 255.5);
--color-primary: oklch(0.783 0.113 253.5);
--color-primary-foreground: oklch(0.160 0.002 286.2);
--color-accent: oklch(0.820 0.149 330.0);
--color-border: oklch(0.527 0.005 258.3);
--color-focus-ring: oklch(0.783 0.113 253.5);
--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.169 0.002 286.2);
--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.169 0.002 286.2);
--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.169 0.002 286.2);
--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.169 0.002 286.2);
}
}Tailwind CSS v4 @theme
@theme {
/* light */
--color-background: oklch(0.975 0.003 247.9); /* #f5f7f9 */
--color-surface: oklch(0.997 0.002 247.8); /* #fdfeff */
--color-text: oklch(0.221 0.005 248.1); /* #191b1d */
--color-text-muted: oklch(0.445 0.007 264.5); /* #525458 */
--color-primary: oklch(0.500 0.161 262.0); /* #2c5dbd */
--color-primary-foreground: oklch(0.991 0.002 247.8); /* #fbfcfd */
--color-accent: oklch(0.521 0.149 330.3); /* #974391 */
--color-border: oklch(0.646 0.006 264.5); /* #8c8e92 */
--color-focus-ring: oklch(0.450 0.160 261.9); /* #1e4eac */
--color-success: oklch(0.5333 0.1163 162.37); /* #008158 */
--color-success-solid: oklch(0.5443 0.1224 160.36); /* #008557 */
--color-success-solid-foreground: oklch(0.991 0.002 247.8); /* #fbfcfd */
--color-warning: oklch(0.5541 0.1212 68.45); /* #a06300 */
--color-warning-solid: oklch(0.8335 0.1508 76.88); /* #ffba44 */
--color-warning-solid-foreground: oklch(0.169 0.002 286.2); /* #0f0f10 */
--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.002 247.8); /* #fbfcfd */
--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.002 247.8); /* #fbfcfd */
/* dark: use as dark:bg-background-dark, or remap under a .dark selector */
--color-background-dark: oklch(0.200 0.004 264.5); /* #151618 */
--color-surface-dark: oklch(0.243 0.004 264.5); /* #1f2022 */
--color-text-dark: oklch(0.949 0.005 275.0); /* #edeef2 */
--color-text-muted-dark: oklch(0.800 0.007 255.5); /* #bbbec2 */
--color-primary-dark: oklch(0.783 0.113 253.5); /* #84bcff */
--color-primary-foreground-dark: oklch(0.160 0.002 286.2); /* #0d0d0e */
--color-accent-dark: oklch(0.820 0.149 330.0); /* #fa9ff2 */
--color-border-dark: oklch(0.527 0.005 258.3); /* #696b6e */
--color-focus-ring-dark: oklch(0.783 0.113 253.5); /* #84bcff */
--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.169 0.002 286.2); /* #0f0f10 */
--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.169 0.002 286.2); /* #0f0f10 */
--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.169 0.002 286.2); /* #0f0f10 */
--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.169 0.002 286.2); /* #0f0f10 */
}shadcn/ui variables (:root and .dark)
:root {
--background: oklch(0.975 0.003 247.9);
--foreground: oklch(0.221 0.005 248.1);
--card: oklch(0.997 0.002 247.8);
--card-foreground: oklch(0.221 0.005 248.1);
--popover: oklch(0.997 0.002 247.8);
--popover-foreground: oklch(0.221 0.005 248.1);
--primary: oklch(0.500 0.161 262.0);
--primary-foreground: oklch(0.991 0.002 247.8);
--secondary: oklch(0.997 0.002 247.8);
--secondary-foreground: oklch(0.221 0.005 248.1);
--muted: oklch(0.997 0.002 247.8);
--muted-foreground: oklch(0.445 0.007 264.5);
--accent: oklch(0.997 0.002 247.8);
--accent-foreground: oklch(0.221 0.005 248.1);
--destructive: oklch(0.551 0.187 27.1);
--destructive-foreground: oklch(0.991 0.002 247.8);
--border: oklch(0.646 0.006 264.5);
--input: oklch(0.646 0.006 264.5);
--ring: oklch(0.450 0.160 261.9);
--success: oklch(0.5443 0.1224 160.36);
--success-foreground: oklch(0.991 0.002 247.8);
--warning: oklch(0.8335 0.1508 76.88);
--warning-foreground: oklch(0.169 0.002 286.2);
--info: oklch(0.5501 0.1324 245.13);
--info-foreground: oklch(0.991 0.002 247.8);
--brand-accent: oklch(0.521 0.149 330.3);
}
.dark {
--background: oklch(0.200 0.004 264.5);
--foreground: oklch(0.949 0.005 275.0);
--card: oklch(0.243 0.004 264.5);
--card-foreground: oklch(0.949 0.005 275.0);
--popover: oklch(0.243 0.004 264.5);
--popover-foreground: oklch(0.949 0.005 275.0);
--primary: oklch(0.783 0.113 253.5);
--primary-foreground: oklch(0.160 0.002 286.2);
--secondary: oklch(0.243 0.004 264.5);
--secondary-foreground: oklch(0.949 0.005 275.0);
--muted: oklch(0.243 0.004 264.5);
--muted-foreground: oklch(0.800 0.007 255.5);
--accent: oklch(0.243 0.004 264.5);
--accent-foreground: oklch(0.949 0.005 275.0);
--destructive: oklch(0.732 0.165 27.5);
--destructive-foreground: oklch(0.169 0.002 286.2);
--border: oklch(0.527 0.005 258.3);
--input: oklch(0.527 0.005 258.3);
--ring: oklch(0.783 0.113 253.5);
--success: oklch(0.759 0.143 165.0);
--success-foreground: oklch(0.169 0.002 286.2);
--warning: oklch(0.7605 0.1539 75.30);
--warning-foreground: oklch(0.169 0.002 286.2);
--info: oklch(0.760 0.132 244.8);
--info-foreground: oklch(0.169 0.002 286.2);
--brand-accent: oklch(0.820 0.149 330.0);
}Design tokens (DTCG-style JSON)
{
"color": {
"light": {
"background": {
"$type": "color",
"$value": "oklch(0.975 0.003 247.9)",
"$description": "sRGB #f5f7f9"
},
"surface": {
"$type": "color",
"$value": "oklch(0.997 0.002 247.8)",
"$description": "sRGB #fdfeff"
},
"text": {
"$type": "color",
"$value": "oklch(0.221 0.005 248.1)",
"$description": "sRGB #191b1d"
},
"text-muted": {
"$type": "color",
"$value": "oklch(0.445 0.007 264.5)",
"$description": "sRGB #525458"
},
"primary": {
"$type": "color",
"$value": "oklch(0.500 0.161 262.0)",
"$description": "sRGB #2c5dbd"
},
"primary-foreground": {
"$type": "color",
"$value": "oklch(0.991 0.002 247.8)",
"$description": "sRGB #fbfcfd"
},
"accent": {
"$type": "color",
"$value": "oklch(0.521 0.149 330.3)",
"$description": "sRGB #974391"
},
"border": {
"$type": "color",
"$value": "oklch(0.646 0.006 264.5)",
"$description": "sRGB #8c8e92"
},
"focus-ring": {
"$type": "color",
"$value": "oklch(0.450 0.160 261.9)",
"$description": "sRGB #1e4eac"
},
"success": {
"$type": "color",
"$value": "oklch(0.5333 0.1163 162.37)",
"$description": "sRGB #008158"
},
"success-solid": {
"$type": "color",
"$value": "oklch(0.5443 0.1224 160.36)",
"$description": "sRGB #008557"
},
"success-solid-foreground": {
"$type": "color",
"$value": "oklch(0.991 0.002 247.8)",
"$description": "sRGB #fbfcfd"
},
"warning": {
"$type": "color",
"$value": "oklch(0.5541 0.1212 68.45)",
"$description": "sRGB #a06300"
},
"warning-solid": {
"$type": "color",
"$value": "oklch(0.8335 0.1508 76.88)",
"$description": "sRGB #ffba44"
},
"warning-solid-foreground": {
"$type": "color",
"$value": "oklch(0.169 0.002 286.2)",
"$description": "sRGB #0f0f10"
},
"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.002 247.8)",
"$description": "sRGB #fbfcfd"
},
"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.002 247.8)",
"$description": "sRGB #fbfcfd"
}
},
"dark": {
"background": {
"$type": "color",
"$value": "oklch(0.200 0.004 264.5)",
"$description": "sRGB #151618"
},
"surface": {
"$type": "color",
"$value": "oklch(0.243 0.004 264.5)",
"$description": "sRGB #1f2022"
},
"text": {
"$type": "color",
"$value": "oklch(0.949 0.005 275.0)",
"$description": "sRGB #edeef2"
},
"text-muted": {
"$type": "color",
"$value": "oklch(0.800 0.007 255.5)",
"$description": "sRGB #bbbec2"
},
"primary": {
"$type": "color",
"$value": "oklch(0.783 0.113 253.5)",
"$description": "sRGB #84bcff"
},
"primary-foreground": {
"$type": "color",
"$value": "oklch(0.160 0.002 286.2)",
"$description": "sRGB #0d0d0e"
},
"accent": {
"$type": "color",
"$value": "oklch(0.820 0.149 330.0)",
"$description": "sRGB #fa9ff2"
},
"border": {
"$type": "color",
"$value": "oklch(0.527 0.005 258.3)",
"$description": "sRGB #696b6e"
},
"focus-ring": {
"$type": "color",
"$value": "oklch(0.783 0.113 253.5)",
"$description": "sRGB #84bcff"
},
"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.169 0.002 286.2)",
"$description": "sRGB #0f0f10"
},
"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.169 0.002 286.2)",
"$description": "sRGB #0f0f10"
},
"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.169 0.002 286.2)",
"$description": "sRGB #0f0f10"
},
"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.169 0.002 286.2)",
"$description": "sRGB #0f0f10"
}
}
}
}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 #2C5DBD in the OKLCH picker →Build a tonal scale →Check a pair
FAQ
- Why include both hex and oklch()?
- oklch() is supported in current browsers, but design tools and older environments still need hex. The CSS export gives hex first and oklch() in an @supports block.
- How do I map this to my token names?
- Rename the roles in the export; the colour values stay the same. The shadcn mapping is an example of that kind of renaming.
- Can I add more steps?
- Yes. Generate a tonal scale with the palette scale tool, then verify the pairs you intend to use.
- Are the DTCG tokens standard-compliant?
- They follow the Design Tokens format's type and value fields with oklch() strings, as the studio's own export does. Tooling support for oklch values varies, so check your pipeline.