OKLCH Studio · Accessible palettes · Special needs

High contrast accessible colour palette for low vision (WCAG AAA)

Updated 2026-10-19

Low vision covers a wide range: blurred sight, tunnel vision, light sensitivity, reduced contrast sensitivity and more. What many of these share is that faint differences are lost first. Pale grey text, thin light borders and subtle hover tints, all common in current interface design, may simply disappear.

This palette sets every text pair at WCAG AAA, 7:1 or better, including muted text, links, buttons and status messages. Borders and focus rings are held higher than the 3:1 that WCAG 1.4.11 requires, and the near-black on near-white pair is used for body text. The result is plain, but it is readable.

AAA is not a level that WCAG recommends for whole sites in every case, because it can be impossible for some content, but for a mode or a product aimed at low-vision users it is a reasonable target. Operating-system high-contrast and forced-colours modes may override your colours entirely, so also test with those.

How to build a palette like this by hand is explained in the accessible colour palette guide; this page is the finished, verified result.

The palette: light and dark

Hex values are the sRGB result after gamut mapping. The oklch() values are those of the final hex, so converting either gives the same colour.

Appointment confirmed

Secondary text sits on the surface colour.

Link in primary · Accent text

Primary buttonSuccessWarningErrorInfo

Light theme

  • Background

    #FDFDFD

    oklch(0.994 0.000 0.0)

    Sample text

  • Surface

    #FEFEFE

    oklch(0.997 0.000 0.0)

    Sample text

  • Text

    #1B1B1B

    oklch(0.222 0.000 0.0)

    Sample text

  • Muted text

    #575757

    oklch(0.457 0.000 0.0)

    Sample text

  • Primary

    #214AC8

    oklch(0.467 0.201 265.2)

    Sample text

  • Primary foreground

    #FCFCFC

    oklch(0.991 0.000 0.0)

    Sample text

  • Accent

    #9A3700

    oklch(0.473 0.143 41.8)

    Sample text

  • Border

    #929292

    oklch(0.660 0.000 0.0)

    Sample text

  • Focus ring

    #1C45C2

    oklch(0.451 0.200 264.9)

    Sample text

  • Success

    #006263

    oklch(0.450 0.077 196.0)

    Sample text

  • Success fill

    #006164

    oklch(0.447 0.076 198.5)

    Sample text

  • Success fill label

    #FCFCFC

    oklch(0.991 0.000 0.0)

    Sample text

  • Warning

    #844800

    oklch(0.465 0.109 60.4)

    Sample text

  • Warning fill

    #FFBA44

    oklch(0.8335 0.1508 76.88)

    Sample text

  • Warning fill label

    #0F0F0F

    oklch(0.168 0.000 0.0)

    Sample text

  • Error

    #A21A1B

    oklch(0.4603 0.1706 26.95)

    Sample text

  • Error fill

    #AE1117

    oklch(0.478 0.186 27.0)

    Sample text

  • Error fill label

    #FCFCFC

    oklch(0.991 0.000 0.0)

    Sample text

  • Info

    #005B94

    oklch(0.457 0.118 246.1)

    Sample text

  • Info fill

    #005999

    oklch(0.455 0.126 249.2)

    Sample text

  • Info fill label

    #FCFCFC

    oklch(0.991 0.000 0.0)

    Sample text

Appointment confirmed

Secondary text sits on the surface colour.

Link in primary · Accent text

Primary buttonSuccessWarningErrorInfo

Dark theme

  • Background

    #060606

    oklch(0.122 0.000 0.0)

    Sample text

  • Surface

    #0E0E0E

    oklch(0.164 0.000 0.0)

    Sample text

  • Text

    #EEEEEE

    oklch(0.949 0.000 0.0)

    Sample text

  • Muted text

    #BEBEBE

    oklch(0.802 0.000 0.0)

    Sample text

  • Primary

    #7EB6FF

    oklch(0.767 0.121 255.4)

    Sample text

  • Primary foreground

    #0D0D0D

    oklch(0.159 0.000 0.0)

    Sample text

  • Accent

    #FFA63D

    oklch(0.797 0.155 66.0)

    Sample text

  • Border

    #636363

    oklch(0.500 0.000 0.0)

    Sample text

  • Focus ring

    #7EB6FF

    oklch(0.767 0.121 255.4)

    Sample text

  • Success

    #55E3E3

    oklch(0.8406 0.1202 195.11)

    Sample text

  • Success fill

    #00CACB

    oklch(0.760 0.130 195.4)

    Sample text

  • Success fill label

    #0F0F0F

    oklch(0.168 0.000 0.0)

    Sample text

  • Warning

    #FFBC56

    oklch(0.839 0.140 75.1)

    Sample text

  • Warning fill

    #E9A11D

    oklch(0.7605 0.1539 75.30)

    Sample text

  • Warning fill label

    #0F0F0F

    oklch(0.168 0.000 0.0)

    Sample text

  • Error

    #FB7266

    oklch(0.714 0.170 27.2)

    Sample text

  • Error fill

    #FF7A6D

    oklch(0.732 0.165 27.5)

    Sample text

  • Error fill label

    #0F0F0F

    oklch(0.168 0.000 0.0)

    Sample text

  • Info

    #71BFFF

    oklch(0.780 0.120 245.0)

    Sample text

  • Info fill

    #61B9FF

    oklch(0.760 0.132 244.8)

    Sample text

  • Info fill label

    #0F0F0F

    oklch(0.168 0.000 0.0)

    Sample text

Contrast table: every verified pair

Pass or fail uses the raw WCAG 2.x ratio; the ratio shown is floored to two decimals so a near miss is never rounded up. APCA Lc is signed: positive is dark on light, negative is light on dark. Each row opens the pair in the checker.

Light theme: 25 pairs
PairWCAG ratioTargetNormal AA / AAALarge AA / AAAAPCA LcAPCA tierCheck
Text on background#1b1b1b on #fdfdfd16.93:1AAA 7:1Pass / PassPass / Pass+102.9Lc 75: body textOpen in checker
Text on surface#1b1b1b on #fefefe17.07:1AAA 7:1Pass / PassPass / Pass+103.5Lc 75: body textOpen in checker
Muted text on background#575757 on #fdfdfd7.10:1AAA 7:1Pass / PassPass / Pass+83.9Lc 75: body textOpen in checker
Muted text on surface#575757 on #fefefe7.16:1AAA 7:1Pass / PassPass / Pass+84.5Lc 75: body textOpen in checker
Primary as link text on background#214ac8 on #fdfdfd7.17:1AAA 7:1Pass / PassPass / Pass+83.4Lc 75: body textOpen in checker
Primary as link text on surface#214ac8 on #fefefe7.23:1AAA 7:1Pass / PassPass / Pass+84.0Lc 75: body textOpen in checker
Button label on primary#fcfcfc on #214ac87.11:1AAA 7:1Pass / PassPass / Pass−87.5Lc 75: body textOpen in checker
Accent text on background#9a3700 on #fdfdfd7.06:1AAA 7:1Pass / PassPass / Pass+82.8Lc 75: body textOpen in checker
Accent text on surface#9a3700 on #fefefe7.12:1AAA 7:1Pass / PassPass / Pass+83.4Lc 75: body textOpen in checker
Border on background#929292 on #fdfdfd3.05:13:1 (WCAG 1.4.11)Fail / FailPass / Fail+56.9Lc 45: headlines and large textOpen in checker
Border on surface#929292 on #fefefe3.08:13:1 (WCAG 1.4.11)Fail / FailPass / Fail+57.5Lc 45: headlines and large textOpen in checker
Focus ring on background#1c45c2 on #fdfdfd7.70:13:1 (WCAG 1.4.11)Pass / PassPass / Pass+85.2Lc 75: body textOpen in checker
Focus ring on surface#1c45c2 on #fefefe7.77:13:1 (WCAG 1.4.11)Pass / PassPass / Pass+85.8Lc 75: body textOpen in checker
Success text on background#006263 on #fdfdfd7.05:1AAA 7:1Pass / PassPass / Pass+83.2Lc 75: body textOpen in checker
Success text on surface#006263 on #fefefe7.11:1AAA 7:1Pass / PassPass / Pass+83.8Lc 75: body textOpen in checker
Success badge label on fill#fcfcfc on #0061647.07:1AAA 7:1Pass / PassPass / Pass−87.6Lc 75: body textOpen in checker
Warning text on background#844800 on #fdfdfd7.09:1AAA 7:1Pass / PassPass / Pass+83.4Lc 75: body textOpen in checker
Warning text on surface#844800 on #fefefe7.15:1AAA 7:1Pass / PassPass / Pass+84.0Lc 75: body textOpen in checker
Warning badge label on fill#0f0f0f on #ffba4411.28:1AAA 7:1Pass / PassPass / Pass+73.2Lc 60: larger content textOpen in checker
Error text on background#a21a1b on #fdfdfd7.64:1AAA 7:1Pass / PassPass / Pass+84.1Lc 75: body textOpen in checker
Error text on surface#a21a1b on #fefefe7.71:1AAA 7:1Pass / PassPass / Pass+84.7Lc 75: body textOpen in checker
Error badge label on fill#fcfcfc on #ae11177.07:1AAA 7:1Pass / PassPass / Pass−86.1Lc 75: body textOpen in checker
Info text on background#005b94 on #fdfdfd7.06:1AAA 7:1Pass / PassPass / Pass+83.1Lc 75: body textOpen in checker
Info text on surface#005b94 on #fefefe7.12:1AAA 7:1Pass / PassPass / Pass+83.7Lc 75: body textOpen in checker
Info badge label on fill#fcfcfc on #0059997.08:1AAA 7:1Pass / PassPass / Pass−87.5Lc 75: body textOpen in checker
Dark theme: 25 pairs
PairWCAG ratioTargetNormal AA / AAALarge AA / AAAAPCA LcAPCA tierCheck
Text on background#eeeeee on #06060617.46:1AAA 7:1Pass / PassPass / Pass−96.7Lc 75: body textOpen in checker
Text on surface#eeeeee on #0e0e0e16.63:1AAA 7:1Pass / PassPass / Pass−96.4Lc 75: body textOpen in checker
Muted text on background#bebebe on #06060610.90:1AAA 7:1Pass / PassPass / Pass−67.4Lc 60: larger content textOpen in checker
Muted text on surface#bebebe on #0e0e0e10.38:1AAA 7:1Pass / PassPass / Pass−67.1Lc 60: larger content textOpen in checker
Primary as link text on background#7eb6ff on #0606069.66:1AAA 7:1Pass / PassPass / Pass−61.4Lc 60: larger content textOpen in checker
Primary as link text on surface#7eb6ff on #0e0e0e9.21:1AAA 7:1Pass / PassPass / Pass−61.1Lc 60: larger content textOpen in checker
Button label on primary#0d0d0d on #7eb6ff9.27:1AAA 7:1Pass / PassPass / Pass+62.7Lc 60: larger content textOpen in checker
Accent text on background#ffa63d on #06060610.39:1AAA 7:1Pass / PassPass / Pass−65.3Lc 60: larger content textOpen in checker
Accent text on surface#ffa63d on #0e0e0e9.90:1AAA 7:1Pass / PassPass / Pass−65.0Lc 60: larger content textOpen in checker
Border on background#636363 on #0606063.37:13:1 (WCAG 1.4.11)Fail / FailPass / Fail−21.7Below Lc 30Open in checker
Border on surface#636363 on #0e0e0e3.21:13:1 (WCAG 1.4.11)Fail / FailPass / Fail−21.5Below Lc 30Open in checker
Focus ring on background#7eb6ff on #0606069.66:13:1 (WCAG 1.4.11)Pass / PassPass / Pass−61.4Lc 60: larger content textOpen in checker
Focus ring on surface#7eb6ff on #0e0e0e9.21:13:1 (WCAG 1.4.11)Pass / PassPass / Pass−61.1Lc 60: larger content textOpen in checker
Success text on background#55e3e3 on #06060613.00:1AAA 7:1Pass / PassPass / Pass−77.8Lc 75: body textOpen in checker
Success text on surface#55e3e3 on #0e0e0e12.39:1AAA 7:1Pass / PassPass / Pass−77.5Lc 75: body textOpen in checker
Success badge label on fill#0f0f0f on #00cacb9.41:1AAA 7:1Pass / PassPass / Pass+64.5Lc 60: larger content textOpen in checker
Warning text on background#ffbc56 on #06060612.13:1AAA 7:1Pass / PassPass / Pass−73.6Lc 60: larger content textOpen in checker
Warning text on surface#ffbc56 on #0e0e0e11.56:1AAA 7:1Pass / PassPass / Pass−73.3Lc 60: larger content textOpen in checker
Warning badge label on fill#0f0f0f on #e9a11d8.74:1AAA 7:1Pass / PassPass / Pass+60.6Lc 60: larger content textOpen in checker
Error text on background#fb7266 on #0606067.43:1AAA 7:1Pass / PassPass / Pass−49.7Lc 45: headlines and large textOpen in checker
Error text on surface#fb7266 on #0e0e0e7.07:1AAA 7:1Pass / PassPass / Pass−49.4Lc 45: headlines and large textOpen in checker
Error badge label on fill#0f0f0f on #ff7a6d7.53:1AAA 7:1Pass / PassPass / Pass+54.2Lc 45: headlines and large textOpen in checker
Info text on background#71bfff on #06060610.22:1AAA 7:1Pass / PassPass / Pass−64.3Lc 60: larger content textOpen in checker
Info text on surface#71bfff on #0e0e0e9.74:1AAA 7:1Pass / PassPass / Pass−64.0Lc 60: larger content textOpen in checker
Info badge label on fill#0f0f0f on #61b9ff9.02:1AAA 7:1Pass / PassPass / Pass+62.1Lc 60: larger content textOpen 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.

Light theme, status fills
PairNormal visionProtanopiaDeuteranopiaTritanopiaHeld to 6
success / warning43.637.348.738.6Shown only
success / error26.420.011.931.1Yes
success / info9.89.69.01.8Shown only
warning / error38.454.248.827.7Shown only
warning / info46.841.351.738.3Shown only
error / info29.325.220.831.2Shown only

Fills as simulated:Normal visionProtanopiaDeuteranopiaTritanopia

Dark theme, status fills
PairNormal visionProtanopiaDeuteranopiaTritanopiaHeld to 6
success / warning24.618.626.428.5Shown only
success / error29.416.216.632.6Yes
success / info10.97.38.14.9Shown only
warning / error13.313.210.55.5Shown only
warning / info28.526.028.726.2Shown only
error / info28.321.820.829.7Shown only

Fills as simulated:Normal visionProtanopiaDeuteranopiaTritanopia

Why these colours

Neutrals have no tint and the page is near-white at lightness 0.995 in the light theme and near-black at 0.12 in the dark theme, which widens the range available to every other role. The primary is a pure, strong blue at OKLCH hue 265 and chroma 0.20 that the solver darkens until white labels reach 7:1.

The accent is an amber-orange at hue 60, and the status roles are all AAA as well. Success was moved toward cyan at hue 195 so that it stays apart from the red-orange error for red-green colour blindness even after both are darkened to reach 7:1. Darkened colours converge in lightness, and the page reports their separation under simulation.

Remember that the largest audience for a high-contrast option is not only people with a diagnosed condition. Many people read in strong sunlight, on old screens or while tired, and benefit from the same margin. Offering the palette as a visible choice, rather than hiding it in settings, makes it easy to find for those who need it in the moment.

The generator solved 50 pairs (25 light, 25 dark), and all of them meet their target on the raw WCAG ratio. The tightest is success text on background in the light theme at 7.05:1 against a target of 7:1. After gamut mapping to sRGB the light primary keeps 100% of the requested chroma; hue was held throughout.

How to use it

  • Use real borders on inputs and buttons, not tints, so every control has a visible edge.
  • Make the focus indicator thick, at least 2 px, with an offset, and keep it in the focus ring colour.
  • Keep links underlined and avoid relying on colour changes for hover.
  • Let users enlarge text to 200 percent without loss, and avoid fixed-height containers.
  • Test with forced-colors mode and the system high-contrast themes, where your CSS colours may be replaced.

Export

Generated from the verified values above, never typed by hand. Light and dark are both included.

CSS custom properties (hex, then oklch())

/* sRGB hex first: every browser. */
:root {
  --color-background: #fdfdfd;
  --color-surface: #fefefe;
  --color-text: #1b1b1b;
  --color-text-muted: #575757;
  --color-primary: #214ac8;
  --color-primary-foreground: #fcfcfc;
  --color-accent: #9a3700;
  --color-border: #929292;
  --color-focus-ring: #1c45c2;
  --color-success: #006263;
  --color-success-solid: #006164;
  --color-success-solid-foreground: #fcfcfc;
  --color-warning: #844800;
  --color-warning-solid: #ffba44;
  --color-warning-solid-foreground: #0f0f0f;
  --color-error: #a21a1b;
  --color-error-solid: #ae1117;
  --color-error-solid-foreground: #fcfcfc;
  --color-info: #005b94;
  --color-info-solid: #005999;
  --color-info-solid-foreground: #fcfcfc;
}
.dark {
  --color-background: #060606;
  --color-surface: #0e0e0e;
  --color-text: #eeeeee;
  --color-text-muted: #bebebe;
  --color-primary: #7eb6ff;
  --color-primary-foreground: #0d0d0d;
  --color-accent: #ffa63d;
  --color-border: #636363;
  --color-focus-ring: #7eb6ff;
  --color-success: #55e3e3;
  --color-success-solid: #00cacb;
  --color-success-solid-foreground: #0f0f0f;
  --color-warning: #ffbc56;
  --color-warning-solid: #e9a11d;
  --color-warning-solid-foreground: #0f0f0f;
  --color-error: #fb7266;
  --color-error-solid: #ff7a6d;
  --color-error-solid-foreground: #0f0f0f;
  --color-info: #71bfff;
  --color-info-solid: #61b9ff;
  --color-info-solid-foreground: #0f0f0f;
}

/* oklch() where supported: the same colours, unclipped values of the hex above. */
@supports (color: oklch(0 0 0)) {
  :root {
    --color-background: oklch(0.994 0.000 0.0);
    --color-surface: oklch(0.997 0.000 0.0);
    --color-text: oklch(0.222 0.000 0.0);
    --color-text-muted: oklch(0.457 0.000 0.0);
    --color-primary: oklch(0.467 0.201 265.2);
    --color-primary-foreground: oklch(0.991 0.000 0.0);
    --color-accent: oklch(0.473 0.143 41.8);
    --color-border: oklch(0.660 0.000 0.0);
    --color-focus-ring: oklch(0.451 0.200 264.9);
    --color-success: oklch(0.450 0.077 196.0);
    --color-success-solid: oklch(0.447 0.076 198.5);
    --color-success-solid-foreground: oklch(0.991 0.000 0.0);
    --color-warning: oklch(0.465 0.109 60.4);
    --color-warning-solid: oklch(0.8335 0.1508 76.88);
    --color-warning-solid-foreground: oklch(0.168 0.000 0.0);
    --color-error: oklch(0.4603 0.1706 26.95);
    --color-error-solid: oklch(0.478 0.186 27.0);
    --color-error-solid-foreground: oklch(0.991 0.000 0.0);
    --color-info: oklch(0.457 0.118 246.1);
    --color-info-solid: oklch(0.455 0.126 249.2);
    --color-info-solid-foreground: oklch(0.991 0.000 0.0);
  }
  .dark {
    --color-background: oklch(0.122 0.000 0.0);
    --color-surface: oklch(0.164 0.000 0.0);
    --color-text: oklch(0.949 0.000 0.0);
    --color-text-muted: oklch(0.802 0.000 0.0);
    --color-primary: oklch(0.767 0.121 255.4);
    --color-primary-foreground: oklch(0.159 0.000 0.0);
    --color-accent: oklch(0.797 0.155 66.0);
    --color-border: oklch(0.500 0.000 0.0);
    --color-focus-ring: oklch(0.767 0.121 255.4);
    --color-success: oklch(0.8406 0.1202 195.11);
    --color-success-solid: oklch(0.760 0.130 195.4);
    --color-success-solid-foreground: oklch(0.168 0.000 0.0);
    --color-warning: oklch(0.839 0.140 75.1);
    --color-warning-solid: oklch(0.7605 0.1539 75.30);
    --color-warning-solid-foreground: oklch(0.168 0.000 0.0);
    --color-error: oklch(0.714 0.170 27.2);
    --color-error-solid: oklch(0.732 0.165 27.5);
    --color-error-solid-foreground: oklch(0.168 0.000 0.0);
    --color-info: oklch(0.780 0.120 245.0);
    --color-info-solid: oklch(0.760 0.132 244.8);
    --color-info-solid-foreground: oklch(0.168 0.000 0.0);
  }
}

Tailwind CSS v4 @theme

@theme {
  /* light */
  --color-background: oklch(0.994 0.000 0.0); /* #fdfdfd */
  --color-surface: oklch(0.997 0.000 0.0); /* #fefefe */
  --color-text: oklch(0.222 0.000 0.0); /* #1b1b1b */
  --color-text-muted: oklch(0.457 0.000 0.0); /* #575757 */
  --color-primary: oklch(0.467 0.201 265.2); /* #214ac8 */
  --color-primary-foreground: oklch(0.991 0.000 0.0); /* #fcfcfc */
  --color-accent: oklch(0.473 0.143 41.8); /* #9a3700 */
  --color-border: oklch(0.660 0.000 0.0); /* #929292 */
  --color-focus-ring: oklch(0.451 0.200 264.9); /* #1c45c2 */
  --color-success: oklch(0.450 0.077 196.0); /* #006263 */
  --color-success-solid: oklch(0.447 0.076 198.5); /* #006164 */
  --color-success-solid-foreground: oklch(0.991 0.000 0.0); /* #fcfcfc */
  --color-warning: oklch(0.465 0.109 60.4); /* #844800 */
  --color-warning-solid: oklch(0.8335 0.1508 76.88); /* #ffba44 */
  --color-warning-solid-foreground: oklch(0.168 0.000 0.0); /* #0f0f0f */
  --color-error: oklch(0.4603 0.1706 26.95); /* #a21a1b */
  --color-error-solid: oklch(0.478 0.186 27.0); /* #ae1117 */
  --color-error-solid-foreground: oklch(0.991 0.000 0.0); /* #fcfcfc */
  --color-info: oklch(0.457 0.118 246.1); /* #005b94 */
  --color-info-solid: oklch(0.455 0.126 249.2); /* #005999 */
  --color-info-solid-foreground: oklch(0.991 0.000 0.0); /* #fcfcfc */
  /* dark: use as dark:bg-background-dark, or remap under a .dark selector */
  --color-background-dark: oklch(0.122 0.000 0.0); /* #060606 */
  --color-surface-dark: oklch(0.164 0.000 0.0); /* #0e0e0e */
  --color-text-dark: oklch(0.949 0.000 0.0); /* #eeeeee */
  --color-text-muted-dark: oklch(0.802 0.000 0.0); /* #bebebe */
  --color-primary-dark: oklch(0.767 0.121 255.4); /* #7eb6ff */
  --color-primary-foreground-dark: oklch(0.159 0.000 0.0); /* #0d0d0d */
  --color-accent-dark: oklch(0.797 0.155 66.0); /* #ffa63d */
  --color-border-dark: oklch(0.500 0.000 0.0); /* #636363 */
  --color-focus-ring-dark: oklch(0.767 0.121 255.4); /* #7eb6ff */
  --color-success-dark: oklch(0.8406 0.1202 195.11); /* #55e3e3 */
  --color-success-solid-dark: oklch(0.760 0.130 195.4); /* #00cacb */
  --color-success-solid-foreground-dark: oklch(0.168 0.000 0.0); /* #0f0f0f */
  --color-warning-dark: oklch(0.839 0.140 75.1); /* #ffbc56 */
  --color-warning-solid-dark: oklch(0.7605 0.1539 75.30); /* #e9a11d */
  --color-warning-solid-foreground-dark: oklch(0.168 0.000 0.0); /* #0f0f0f */
  --color-error-dark: oklch(0.714 0.170 27.2); /* #fb7266 */
  --color-error-solid-dark: oklch(0.732 0.165 27.5); /* #ff7a6d */
  --color-error-solid-foreground-dark: oklch(0.168 0.000 0.0); /* #0f0f0f */
  --color-info-dark: oklch(0.780 0.120 245.0); /* #71bfff */
  --color-info-solid-dark: oklch(0.760 0.132 244.8); /* #61b9ff */
  --color-info-solid-foreground-dark: oklch(0.168 0.000 0.0); /* #0f0f0f */
}

shadcn/ui variables (:root and .dark)

:root {
  --background: oklch(0.994 0.000 0.0);
  --foreground: oklch(0.222 0.000 0.0);
  --card: oklch(0.997 0.000 0.0);
  --card-foreground: oklch(0.222 0.000 0.0);
  --popover: oklch(0.997 0.000 0.0);
  --popover-foreground: oklch(0.222 0.000 0.0);
  --primary: oklch(0.467 0.201 265.2);
  --primary-foreground: oklch(0.991 0.000 0.0);
  --secondary: oklch(0.997 0.000 0.0);
  --secondary-foreground: oklch(0.222 0.000 0.0);
  --muted: oklch(0.997 0.000 0.0);
  --muted-foreground: oklch(0.457 0.000 0.0);
  --accent: oklch(0.997 0.000 0.0);
  --accent-foreground: oklch(0.222 0.000 0.0);
  --destructive: oklch(0.478 0.186 27.0);
  --destructive-foreground: oklch(0.991 0.000 0.0);
  --border: oklch(0.660 0.000 0.0);
  --input: oklch(0.660 0.000 0.0);
  --ring: oklch(0.451 0.200 264.9);
  --success: oklch(0.447 0.076 198.5);
  --success-foreground: oklch(0.991 0.000 0.0);
  --warning: oklch(0.8335 0.1508 76.88);
  --warning-foreground: oklch(0.168 0.000 0.0);
  --info: oklch(0.455 0.126 249.2);
  --info-foreground: oklch(0.991 0.000 0.0);
  --brand-accent: oklch(0.473 0.143 41.8);
}

.dark {
  --background: oklch(0.122 0.000 0.0);
  --foreground: oklch(0.949 0.000 0.0);
  --card: oklch(0.164 0.000 0.0);
  --card-foreground: oklch(0.949 0.000 0.0);
  --popover: oklch(0.164 0.000 0.0);
  --popover-foreground: oklch(0.949 0.000 0.0);
  --primary: oklch(0.767 0.121 255.4);
  --primary-foreground: oklch(0.159 0.000 0.0);
  --secondary: oklch(0.164 0.000 0.0);
  --secondary-foreground: oklch(0.949 0.000 0.0);
  --muted: oklch(0.164 0.000 0.0);
  --muted-foreground: oklch(0.802 0.000 0.0);
  --accent: oklch(0.164 0.000 0.0);
  --accent-foreground: oklch(0.949 0.000 0.0);
  --destructive: oklch(0.732 0.165 27.5);
  --destructive-foreground: oklch(0.168 0.000 0.0);
  --border: oklch(0.500 0.000 0.0);
  --input: oklch(0.500 0.000 0.0);
  --ring: oklch(0.767 0.121 255.4);
  --success: oklch(0.760 0.130 195.4);
  --success-foreground: oklch(0.168 0.000 0.0);
  --warning: oklch(0.7605 0.1539 75.30);
  --warning-foreground: oklch(0.168 0.000 0.0);
  --info: oklch(0.760 0.132 244.8);
  --info-foreground: oklch(0.168 0.000 0.0);
  --brand-accent: oklch(0.797 0.155 66.0);
}

Design tokens (DTCG-style JSON)

{
  "color": {
    "light": {
      "background": {
        "$type": "color",
        "$value": "oklch(0.994 0.000 0.0)",
        "$description": "sRGB #fdfdfd"
      },
      "surface": {
        "$type": "color",
        "$value": "oklch(0.997 0.000 0.0)",
        "$description": "sRGB #fefefe"
      },
      "text": {
        "$type": "color",
        "$value": "oklch(0.222 0.000 0.0)",
        "$description": "sRGB #1b1b1b"
      },
      "text-muted": {
        "$type": "color",
        "$value": "oklch(0.457 0.000 0.0)",
        "$description": "sRGB #575757"
      },
      "primary": {
        "$type": "color",
        "$value": "oklch(0.467 0.201 265.2)",
        "$description": "sRGB #214ac8"
      },
      "primary-foreground": {
        "$type": "color",
        "$value": "oklch(0.991 0.000 0.0)",
        "$description": "sRGB #fcfcfc"
      },
      "accent": {
        "$type": "color",
        "$value": "oklch(0.473 0.143 41.8)",
        "$description": "sRGB #9a3700"
      },
      "border": {
        "$type": "color",
        "$value": "oklch(0.660 0.000 0.0)",
        "$description": "sRGB #929292"
      },
      "focus-ring": {
        "$type": "color",
        "$value": "oklch(0.451 0.200 264.9)",
        "$description": "sRGB #1c45c2"
      },
      "success": {
        "$type": "color",
        "$value": "oklch(0.450 0.077 196.0)",
        "$description": "sRGB #006263"
      },
      "success-solid": {
        "$type": "color",
        "$value": "oklch(0.447 0.076 198.5)",
        "$description": "sRGB #006164"
      },
      "success-solid-foreground": {
        "$type": "color",
        "$value": "oklch(0.991 0.000 0.0)",
        "$description": "sRGB #fcfcfc"
      },
      "warning": {
        "$type": "color",
        "$value": "oklch(0.465 0.109 60.4)",
        "$description": "sRGB #844800"
      },
      "warning-solid": {
        "$type": "color",
        "$value": "oklch(0.8335 0.1508 76.88)",
        "$description": "sRGB #ffba44"
      },
      "warning-solid-foreground": {
        "$type": "color",
        "$value": "oklch(0.168 0.000 0.0)",
        "$description": "sRGB #0f0f0f"
      },
      "error": {
        "$type": "color",
        "$value": "oklch(0.4603 0.1706 26.95)",
        "$description": "sRGB #a21a1b"
      },
      "error-solid": {
        "$type": "color",
        "$value": "oklch(0.478 0.186 27.0)",
        "$description": "sRGB #ae1117"
      },
      "error-solid-foreground": {
        "$type": "color",
        "$value": "oklch(0.991 0.000 0.0)",
        "$description": "sRGB #fcfcfc"
      },
      "info": {
        "$type": "color",
        "$value": "oklch(0.457 0.118 246.1)",
        "$description": "sRGB #005b94"
      },
      "info-solid": {
        "$type": "color",
        "$value": "oklch(0.455 0.126 249.2)",
        "$description": "sRGB #005999"
      },
      "info-solid-foreground": {
        "$type": "color",
        "$value": "oklch(0.991 0.000 0.0)",
        "$description": "sRGB #fcfcfc"
      }
    },
    "dark": {
      "background": {
        "$type": "color",
        "$value": "oklch(0.122 0.000 0.0)",
        "$description": "sRGB #060606"
      },
      "surface": {
        "$type": "color",
        "$value": "oklch(0.164 0.000 0.0)",
        "$description": "sRGB #0e0e0e"
      },
      "text": {
        "$type": "color",
        "$value": "oklch(0.949 0.000 0.0)",
        "$description": "sRGB #eeeeee"
      },
      "text-muted": {
        "$type": "color",
        "$value": "oklch(0.802 0.000 0.0)",
        "$description": "sRGB #bebebe"
      },
      "primary": {
        "$type": "color",
        "$value": "oklch(0.767 0.121 255.4)",
        "$description": "sRGB #7eb6ff"
      },
      "primary-foreground": {
        "$type": "color",
        "$value": "oklch(0.159 0.000 0.0)",
        "$description": "sRGB #0d0d0d"
      },
      "accent": {
        "$type": "color",
        "$value": "oklch(0.797 0.155 66.0)",
        "$description": "sRGB #ffa63d"
      },
      "border": {
        "$type": "color",
        "$value": "oklch(0.500 0.000 0.0)",
        "$description": "sRGB #636363"
      },
      "focus-ring": {
        "$type": "color",
        "$value": "oklch(0.767 0.121 255.4)",
        "$description": "sRGB #7eb6ff"
      },
      "success": {
        "$type": "color",
        "$value": "oklch(0.8406 0.1202 195.11)",
        "$description": "sRGB #55e3e3"
      },
      "success-solid": {
        "$type": "color",
        "$value": "oklch(0.760 0.130 195.4)",
        "$description": "sRGB #00cacb"
      },
      "success-solid-foreground": {
        "$type": "color",
        "$value": "oklch(0.168 0.000 0.0)",
        "$description": "sRGB #0f0f0f"
      },
      "warning": {
        "$type": "color",
        "$value": "oklch(0.839 0.140 75.1)",
        "$description": "sRGB #ffbc56"
      },
      "warning-solid": {
        "$type": "color",
        "$value": "oklch(0.7605 0.1539 75.30)",
        "$description": "sRGB #e9a11d"
      },
      "warning-solid-foreground": {
        "$type": "color",
        "$value": "oklch(0.168 0.000 0.0)",
        "$description": "sRGB #0f0f0f"
      },
      "error": {
        "$type": "color",
        "$value": "oklch(0.714 0.170 27.2)",
        "$description": "sRGB #fb7266"
      },
      "error-solid": {
        "$type": "color",
        "$value": "oklch(0.732 0.165 27.5)",
        "$description": "sRGB #ff7a6d"
      },
      "error-solid-foreground": {
        "$type": "color",
        "$value": "oklch(0.168 0.000 0.0)",
        "$description": "sRGB #0f0f0f"
      },
      "info": {
        "$type": "color",
        "$value": "oklch(0.780 0.120 245.0)",
        "$description": "sRGB #71bfff"
      },
      "info-solid": {
        "$type": "color",
        "$value": "oklch(0.760 0.132 244.8)",
        "$description": "sRGB #61b9ff"
      },
      "info-solid-foreground": {
        "$type": "color",
        "$value": "oklch(0.168 0.000 0.0)",
        "$description": "sRGB #0f0f0f"
      }
    }
  }
}

Open in the studio

Take the primary colour into the picker, build a tonal scale from it, or check any pair of your own.

Open #214AC8 in the OKLCH picker →Build a tonal scale →Check a pair

FAQ

Does high contrast mean pure black on pure white?
Not necessarily. Some users with light sensitivity find the maximum contrast uncomfortable. This palette lets the solver choose the nearest passing value, and offers a dark theme.
Is 7:1 required by WCAG?
No. AA, 4.5:1, is the usual requirement; 7:1 is the AAA level in Success Criterion 1.4.6. Targeting it is optional.
How does this relate to the dark theme?
The dark theme uses a near-black background and light text with the same AAA targets, so users who prefer light-on-dark also get 7:1 pairs.
What about the APCA numbers?
They are listed beside each WCAG ratio. Most text pairs reach the Lc 75 body-text tier or beyond in the light theme, and dark pairs show negative signs.

← All accessible palettes