OKLCH Studio · Accessible palettes · Product & UI
Accessible color palette for SaaS dashboards (WCAG 2.2 verified)
Updated 2026-10-19
A SaaS dashboard is a dense screen that people look at for hours. It stacks cards, tables, filters, tabs and charts, and nearly every element carries a state: selected, hovered, disabled, loading, failed. The accessibility risk is not one dramatic failure but a hundred small low-contrast details, such as pale grey column headers or faint dividers, that add up to fatigue and misreads.
This palette gives each part of that interface a named, verified role. Body text and table cell text are at AAA, muted text for column headers and hints at AA, and borders and focus rings at 3:1 against both the page and the card surface. Where cards sit on a page background, both pairs are listed, because users see both.
WCAG 2.2 AA is the common baseline in B2B procurement questionnaires, and many buyers ask for an accessibility conformance report. Colour contrast is a small part of that, but it is the part you can verify automatically, which is what this page does.
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
#F4F7FC
oklch(0.975 0.007 260.7)
Sample text
- Surface
#FBFEFF
oklch(0.995 0.003 219.5)
Sample text
- Text
#181B1F
oklch(0.221 0.009 255.6)
Sample text
- Muted text
#55585F
oklch(0.460 0.012 267.3)
Sample text
- Primary
#355BBD
oklch(0.499 0.160 265.1)
Sample text
- Primary foreground
#FBFCFE
oklch(0.991 0.003 264.5)
Sample text
- Accent
#007B66
oklch(0.520 0.098 175.2)
Sample text
- Border
#8B8E96
oklch(0.647 0.012 269.5)
Sample text
- Focus ring
#284CAC
oklch(0.449 0.159 265.0)
Sample text
- Success
#008158
oklch(0.5333 0.1163 162.37)
Sample text
- Success fill
#008558
oklch(0.545 0.122 160.9)
Sample text
- Success fill label
#FBFCFE
oklch(0.991 0.003 264.5)
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
#0F0F11
oklch(0.169 0.004 286.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
#FBFCFE
oklch(0.991 0.003 264.5)
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
#FBFCFE
oklch(0.991 0.003 264.5)
Sample text
Appointment confirmed
Secondary text sits on the surface colour.
Link in primary · Accent text
Dark theme
- Background
#14161A
oklch(0.200 0.009 264.4)
Sample text
- Surface
#1F2024
oklch(0.244 0.008 274.6)
Sample text
- Text
#EBEEF5
oklch(0.949 0.010 267.4)
Sample text
- Muted text
#BABEC6
oklch(0.801 0.012 264.5)
Sample text
- Primary
#8BBAFF
oklch(0.782 0.111 257.9)
Sample text
- Primary foreground
#0D0D0F
oklch(0.160 0.004 285.9)
Sample text
- Accent
#78DAC0
oklch(0.821 0.101 174.5)
Sample text
- Border
#686B73
oklch(0.528 0.013 269.4)
Sample text
- Focus ring
#8BBAFF
oklch(0.782 0.111 257.9)
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
#0F0F11
oklch(0.169 0.004 286.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
#0F0F11
oklch(0.169 0.004 286.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
#0F0F11
oklch(0.169 0.004 286.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
#0F0F11
oklch(0.169 0.004 286.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#181b1f on #f4f7fc | 16.09:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +99.2 | Lc 75: body text | Open in checker |
| Text on surface#181b1f on #fbfeff | 17.05:1 | AAA 7:1 | Pass / Pass | Pass / Pass | +103.2 | Lc 75: body text | Open in checker |
| Muted text on background#55585f on #f4f7fc | 6.63:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +79.8 | Lc 75: body text | Open in checker |
| Muted text on surface#55585f on #fbfeff | 7.03:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +83.8 | Lc 75: body text | Open in checker |
| Primary as link text on background#355bbd on #f4f7fc | 5.78:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +75.6 | Lc 75: body text | Open in checker |
| Primary as link text on surface#355bbd on #fbfeff | 6.12:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +79.6 | Lc 75: body text | Open in checker |
| Button label on primary#fbfcfe on #355bbd | 6.04:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | −83.7 | Lc 75: body text | Open in checker |
| Accent text on background#007b66 on #f4f7fc | 4.85:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +70.4 | Lc 60: larger content text | Open in checker |
| Accent text on surface#007b66 on #fbfeff | 5.14:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +74.4 | Lc 60: larger content text | Open in checker |
| Border on background#8b8e96 on #f4f7fc | 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#8b8e96 on #fbfeff | 3.23:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | +59.2 | Lc 45: headlines and large text | Open in checker |
| Focus ring on background#284cac on #f4f7fc | 7.19:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | +81.4 | Lc 75: body text | Open in checker |
| Focus ring on surface#284cac on #fbfeff | 7.61: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 #f4f7fc | 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 #fbfeff | 4.84:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +72.4 | Lc 60: larger content text | Open in checker |
| Success badge label on fill#fbfcfe 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#a06300 on #f4f7fc | 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 #fbfeff | 4.84:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +72.7 | Lc 60: larger content text | Open in checker |
| Warning badge label on fill#0f0f11 on #ffba44 | 11.26: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 #f4f7fc | 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 #fbfeff | 7.67:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +84.3 | Lc 75: body text | Open in checker |
| Error badge label on fill#fbfcfe 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 #f4f7fc | 4.67:1 | AA 4.5:1 | Pass / Fail | Pass / Pass | +69.4 | Lc 60: larger content text | Open in checker |
| Info text on surface#2274af on #fbfeff | 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#fbfcfe 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#ebeef5 on #14161a | 15.59:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −95.8 | Lc 75: body text | Open in checker |
| Text on surface#ebeef5 on #1f2024 | 14.01:1 | AAA 7:1 | Pass / Pass | Pass / Pass | −94.5 | Lc 75: body text | Open in checker |
| Muted text on background#babec6 on #14161a | 9.71:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −66.4 | Lc 60: larger content text | Open in checker |
| Muted text on surface#babec6 on #1f2024 | 8.73:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −65.2 | Lc 60: larger content text | Open in checker |
| Primary as link text on background#8bbaff on #14161a | 9.11:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −63.1 | Lc 60: larger content text | Open in checker |
| Primary as link text on surface#8bbaff on #1f2024 | 8.18:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −61.9 | Lc 60: larger content text | Open in checker |
| Button label on primary#0d0d0f on #8bbaff | 9.76:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | +65.2 | Lc 60: larger content text | Open in checker |
| Accent text on background#78dac0 on #14161a | 10.85:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −72.8 | Lc 60: larger content text | Open in checker |
| Accent text on surface#78dac0 on #1f2024 | 9.75:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −71.5 | Lc 60: larger content text | Open in checker |
| Border on background#686b73 on #14161a | 3.39:1 | 3:1 (WCAG 1.4.11) | Fail / Fail | Pass / Fail | −24.3 | Below Lc 30 | Open in checker |
| Border on surface#686b73 on #1f2024 | 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#8bbaff on #14161a | 9.11:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | −63.1 | Lc 60: larger content text | Open in checker |
| Focus ring on surface#8bbaff on #1f2024 | 8.18:1 | 3:1 (WCAG 1.4.11) | Pass / Pass | Pass / Pass | −61.9 | Lc 60: larger content text | Open in checker |
| Success text on background#6ce5b5 on #14161a | 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 #1f2024 | 10.47:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −75.8 | Lc 75: body text | Open in checker |
| Success badge label on fill#0f0f11 on #3bcd9a | 9.46: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 #14161a | 10.85: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 #1f2024 | 9.74:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −71.5 | Lc 60: larger content text | Open in checker |
| Warning badge label on fill#0f0f11 on #e9a11d | 8.73: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 #14161a | 6.28: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 #1f2024 | 5.64: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#0f0f11 on #ff7a6d | 7.52: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 #14161a | 9.14: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 #1f2024 | 8.21:1 | AA 4.5:1 | Pass / Pass | Pass / Pass | −62.2 | Lc 60: larger content text | Open in checker |
| Info badge label on fill#0f0f11 on #61b9ff | 9.01: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 | 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 an indigo-blue at OKLCH hue 265 and chroma 0.16. It is an established product colour, strongly distinct from the red, amber and green status hues, and it remains vivid after being darkened for AA. The teal accent at hue 175 handles secondary highlights such as a selected row or a new feature marker.
Neutrals have a faint indigo tint at chroma 0.008. In a dashboard they cover most of the pixels, and a slight tint ties tables and cards to the brand. The lightness of each role was solved against both background and card, so data tables inside cards are as safe as text on the page itself.
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
- Use the border colour for table dividers only if you need them for comprehension; otherwise use spacing, since a 3:1 divider is visually heavy.
- Set numeric columns in the full text colour and right-align them; use the muted colour for units and headers.
- Show status with a chip that has a label and a leading icon, using the verified badge pairs.
- Keep the primary for the one main action per card or page, and use text buttons in the primary text colour for others.
- Take chart series colours from the data visualization palette, not from the status chips.
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: #f4f7fc;
--color-surface: #fbfeff;
--color-text: #181b1f;
--color-text-muted: #55585f;
--color-primary: #355bbd;
--color-primary-foreground: #fbfcfe;
--color-accent: #007b66;
--color-border: #8b8e96;
--color-focus-ring: #284cac;
--color-success: #008158;
--color-success-solid: #008558;
--color-success-solid-foreground: #fbfcfe;
--color-warning: #a06300;
--color-warning-solid: #ffba44;
--color-warning-solid-foreground: #0f0f11;
--color-error: #a21a1b;
--color-error-solid: #c8322e;
--color-error-solid-foreground: #fbfcfe;
--color-info: #2274af;
--color-info-solid: #1377b9;
--color-info-solid-foreground: #fbfcfe;
}
.dark {
--color-background: #14161a;
--color-surface: #1f2024;
--color-text: #ebeef5;
--color-text-muted: #babec6;
--color-primary: #8bbaff;
--color-primary-foreground: #0d0d0f;
--color-accent: #78dac0;
--color-border: #686b73;
--color-focus-ring: #8bbaff;
--color-success: #6ce5b5;
--color-success-solid: #3bcd9a;
--color-success-solid-foreground: #0f0f11;
--color-warning: #ffbc56;
--color-warning-solid: #e9a11d;
--color-warning-solid-foreground: #0f0f11;
--color-error: #f66d62;
--color-error-solid: #ff7a6d;
--color-error-solid-foreground: #0f0f11;
--color-info: #71bfff;
--color-info-solid: #61b9ff;
--color-info-solid-foreground: #0f0f11;
}
/* 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.007 260.7);
--color-surface: oklch(0.995 0.003 219.5);
--color-text: oklch(0.221 0.009 255.6);
--color-text-muted: oklch(0.460 0.012 267.3);
--color-primary: oklch(0.499 0.160 265.1);
--color-primary-foreground: oklch(0.991 0.003 264.5);
--color-accent: oklch(0.520 0.098 175.2);
--color-border: oklch(0.647 0.012 269.5);
--color-focus-ring: oklch(0.449 0.159 265.0);
--color-success: oklch(0.5333 0.1163 162.37);
--color-success-solid: oklch(0.545 0.122 160.9);
--color-success-solid-foreground: oklch(0.991 0.003 264.5);
--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.004 286.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.003 264.5);
--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.003 264.5);
}
.dark {
--color-background: oklch(0.200 0.009 264.4);
--color-surface: oklch(0.244 0.008 274.6);
--color-text: oklch(0.949 0.010 267.4);
--color-text-muted: oklch(0.801 0.012 264.5);
--color-primary: oklch(0.782 0.111 257.9);
--color-primary-foreground: oklch(0.160 0.004 285.9);
--color-accent: oklch(0.821 0.101 174.5);
--color-border: oklch(0.528 0.013 269.4);
--color-focus-ring: oklch(0.782 0.111 257.9);
--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.004 286.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.169 0.004 286.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.169 0.004 286.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.169 0.004 286.0);
}
}Tailwind CSS v4 @theme
@theme {
/* light */
--color-background: oklch(0.975 0.007 260.7); /* #f4f7fc */
--color-surface: oklch(0.995 0.003 219.5); /* #fbfeff */
--color-text: oklch(0.221 0.009 255.6); /* #181b1f */
--color-text-muted: oklch(0.460 0.012 267.3); /* #55585f */
--color-primary: oklch(0.499 0.160 265.1); /* #355bbd */
--color-primary-foreground: oklch(0.991 0.003 264.5); /* #fbfcfe */
--color-accent: oklch(0.520 0.098 175.2); /* #007b66 */
--color-border: oklch(0.647 0.012 269.5); /* #8b8e96 */
--color-focus-ring: oklch(0.449 0.159 265.0); /* #284cac */
--color-success: oklch(0.5333 0.1163 162.37); /* #008158 */
--color-success-solid: oklch(0.545 0.122 160.9); /* #008558 */
--color-success-solid-foreground: oklch(0.991 0.003 264.5); /* #fbfcfe */
--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.004 286.0); /* #0f0f11 */
--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.003 264.5); /* #fbfcfe */
--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.003 264.5); /* #fbfcfe */
/* dark: use as dark:bg-background-dark, or remap under a .dark selector */
--color-background-dark: oklch(0.200 0.009 264.4); /* #14161a */
--color-surface-dark: oklch(0.244 0.008 274.6); /* #1f2024 */
--color-text-dark: oklch(0.949 0.010 267.4); /* #ebeef5 */
--color-text-muted-dark: oklch(0.801 0.012 264.5); /* #babec6 */
--color-primary-dark: oklch(0.782 0.111 257.9); /* #8bbaff */
--color-primary-foreground-dark: oklch(0.160 0.004 285.9); /* #0d0d0f */
--color-accent-dark: oklch(0.821 0.101 174.5); /* #78dac0 */
--color-border-dark: oklch(0.528 0.013 269.4); /* #686b73 */
--color-focus-ring-dark: oklch(0.782 0.111 257.9); /* #8bbaff */
--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.004 286.0); /* #0f0f11 */
--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.004 286.0); /* #0f0f11 */
--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.004 286.0); /* #0f0f11 */
--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.004 286.0); /* #0f0f11 */
}shadcn/ui variables (:root and .dark)
:root {
--background: oklch(0.975 0.007 260.7);
--foreground: oklch(0.221 0.009 255.6);
--card: oklch(0.995 0.003 219.5);
--card-foreground: oklch(0.221 0.009 255.6);
--popover: oklch(0.995 0.003 219.5);
--popover-foreground: oklch(0.221 0.009 255.6);
--primary: oklch(0.499 0.160 265.1);
--primary-foreground: oklch(0.991 0.003 264.5);
--secondary: oklch(0.995 0.003 219.5);
--secondary-foreground: oklch(0.221 0.009 255.6);
--muted: oklch(0.995 0.003 219.5);
--muted-foreground: oklch(0.460 0.012 267.3);
--accent: oklch(0.995 0.003 219.5);
--accent-foreground: oklch(0.221 0.009 255.6);
--destructive: oklch(0.551 0.187 27.1);
--destructive-foreground: oklch(0.991 0.003 264.5);
--border: oklch(0.647 0.012 269.5);
--input: oklch(0.647 0.012 269.5);
--ring: oklch(0.449 0.159 265.0);
--success: oklch(0.545 0.122 160.9);
--success-foreground: oklch(0.991 0.003 264.5);
--warning: oklch(0.8335 0.1508 76.88);
--warning-foreground: oklch(0.169 0.004 286.0);
--info: oklch(0.5501 0.1324 245.13);
--info-foreground: oklch(0.991 0.003 264.5);
--brand-accent: oklch(0.520 0.098 175.2);
}
.dark {
--background: oklch(0.200 0.009 264.4);
--foreground: oklch(0.949 0.010 267.4);
--card: oklch(0.244 0.008 274.6);
--card-foreground: oklch(0.949 0.010 267.4);
--popover: oklch(0.244 0.008 274.6);
--popover-foreground: oklch(0.949 0.010 267.4);
--primary: oklch(0.782 0.111 257.9);
--primary-foreground: oklch(0.160 0.004 285.9);
--secondary: oklch(0.244 0.008 274.6);
--secondary-foreground: oklch(0.949 0.010 267.4);
--muted: oklch(0.244 0.008 274.6);
--muted-foreground: oklch(0.801 0.012 264.5);
--accent: oklch(0.244 0.008 274.6);
--accent-foreground: oklch(0.949 0.010 267.4);
--destructive: oklch(0.732 0.165 27.5);
--destructive-foreground: oklch(0.169 0.004 286.0);
--border: oklch(0.528 0.013 269.4);
--input: oklch(0.528 0.013 269.4);
--ring: oklch(0.782 0.111 257.9);
--success: oklch(0.759 0.143 165.0);
--success-foreground: oklch(0.169 0.004 286.0);
--warning: oklch(0.7605 0.1539 75.30);
--warning-foreground: oklch(0.169 0.004 286.0);
--info: oklch(0.760 0.132 244.8);
--info-foreground: oklch(0.169 0.004 286.0);
--brand-accent: oklch(0.821 0.101 174.5);
}Design tokens (DTCG-style JSON)
{
"color": {
"light": {
"background": {
"$type": "color",
"$value": "oklch(0.975 0.007 260.7)",
"$description": "sRGB #f4f7fc"
},
"surface": {
"$type": "color",
"$value": "oklch(0.995 0.003 219.5)",
"$description": "sRGB #fbfeff"
},
"text": {
"$type": "color",
"$value": "oklch(0.221 0.009 255.6)",
"$description": "sRGB #181b1f"
},
"text-muted": {
"$type": "color",
"$value": "oklch(0.460 0.012 267.3)",
"$description": "sRGB #55585f"
},
"primary": {
"$type": "color",
"$value": "oklch(0.499 0.160 265.1)",
"$description": "sRGB #355bbd"
},
"primary-foreground": {
"$type": "color",
"$value": "oklch(0.991 0.003 264.5)",
"$description": "sRGB #fbfcfe"
},
"accent": {
"$type": "color",
"$value": "oklch(0.520 0.098 175.2)",
"$description": "sRGB #007b66"
},
"border": {
"$type": "color",
"$value": "oklch(0.647 0.012 269.5)",
"$description": "sRGB #8b8e96"
},
"focus-ring": {
"$type": "color",
"$value": "oklch(0.449 0.159 265.0)",
"$description": "sRGB #284cac"
},
"success": {
"$type": "color",
"$value": "oklch(0.5333 0.1163 162.37)",
"$description": "sRGB #008158"
},
"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.003 264.5)",
"$description": "sRGB #fbfcfe"
},
"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.004 286.0)",
"$description": "sRGB #0f0f11"
},
"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.003 264.5)",
"$description": "sRGB #fbfcfe"
},
"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.003 264.5)",
"$description": "sRGB #fbfcfe"
}
},
"dark": {
"background": {
"$type": "color",
"$value": "oklch(0.200 0.009 264.4)",
"$description": "sRGB #14161a"
},
"surface": {
"$type": "color",
"$value": "oklch(0.244 0.008 274.6)",
"$description": "sRGB #1f2024"
},
"text": {
"$type": "color",
"$value": "oklch(0.949 0.010 267.4)",
"$description": "sRGB #ebeef5"
},
"text-muted": {
"$type": "color",
"$value": "oklch(0.801 0.012 264.5)",
"$description": "sRGB #babec6"
},
"primary": {
"$type": "color",
"$value": "oklch(0.782 0.111 257.9)",
"$description": "sRGB #8bbaff"
},
"primary-foreground": {
"$type": "color",
"$value": "oklch(0.160 0.004 285.9)",
"$description": "sRGB #0d0d0f"
},
"accent": {
"$type": "color",
"$value": "oklch(0.821 0.101 174.5)",
"$description": "sRGB #78dac0"
},
"border": {
"$type": "color",
"$value": "oklch(0.528 0.013 269.4)",
"$description": "sRGB #686b73"
},
"focus-ring": {
"$type": "color",
"$value": "oklch(0.782 0.111 257.9)",
"$description": "sRGB #8bbaff"
},
"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.004 286.0)",
"$description": "sRGB #0f0f11"
},
"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.004 286.0)",
"$description": "sRGB #0f0f11"
},
"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.004 286.0)",
"$description": "sRGB #0f0f11"
},
"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.004 286.0)",
"$description": "sRGB #0f0f11"
}
}
}
}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 #355BBD in the OKLCH picker →Build a tonal scale →Check a pair
FAQ
- Why are borders so dark?
- WCAG 1.4.11 asks for 3:1 for visual information needed to identify a component, such as an input boundary. Decorative dividers do not need to meet it, so use a lighter colour only for those.
- Does this work with Tailwind?
- Yes. The export includes a Tailwind v4 @theme block with the OKLCH values and hex comments. See also the Tailwind AA research for how the default palette fares.
- Can I use it with shadcn/ui?
- Yes, the shadcn block maps to the standard variable names, plus extra success, warning and info variables.
- How do I handle hover and disabled states?
- Disabled controls are exempt from WCAG contrast rules, but hover states on enabled controls are not. Check any hover colour with the contrast checker.