Back to gallery
Reference & playground / 2026

Color science,
playable.

OKLCH, contrast, harmonies, gamut, and the dumb little pitfalls that quietly kill a design. Drag every slider. The page itself is a proof of the rules.

Perceptual lightness WCAG 2.2 Interactive

Color on the web is not pigment. It is math, biology, and a long chain of assumptions about the sRGB display you are probably not looking at. Most of the “that palette feels off” moments come from one of four places — uneven perceived lightness, failed contrast, simultaneous-contrast vibration, and pure-white halation.

This page demonstrates the fixes instead of describing them. Every slider, swatch, and wheel is live. The companion write-up at COLOR-SCIENCE.md has the theory and citations; this is the playground.

I. Perceptual uniformity

OKLCH has even L. HSL never did.

Sweep the hue wheel at one fixed “lightness.” In HSL, yellow blows out and blue goes midnight — L is a math trick on RGB, not perceived brightness. In OKLCH, L is perceptually uniform. Move the slider. Watch the top row flicker; watch the bottom stay calm.

HSL 70%  /  OKLCH 0.70
HSL — same L, uneven perceived brightness yellows blow out, blues go dark
OKLCH — same L, stable perceived brightness every hue feels equally bright
II. WCAG 2.2 contrast

Two colors walk into a ratio.

The WCAG 2 contrast formula compares relative luminance. Body text wants 4.5:1. Large text wants 3:1. Non-text UI wants 3:1. AAA wants 7:1. Pick two colors, test a preset, feel the math.

Foreground
The quick brown fox jumps over the lazy dog.
Tiny caption text. 13px Inter.
Background
The quick brown fox jumps over the lazy dog.
Tiny caption text. 13px Inter.
13.51
Contrast ratio
higher = easier to read
III. The 60 / 30 / 10 rule

Three proportions, one mood.

A starting pose: 60% dominant surface, 30% secondary, 10% accent. This page runs the pink-dominant version. Flip to blue-dominant and feel the room change from spa to lagoon.

60%cream surface
30%salt pink
10%sea accent
IV. Harmonies

The wheel, and why split-complement saves you.

True complementaries sit 180° apart and physically vibrate at a shared edge (simultaneous contrast: your cones don't like it). Split-complement shifts the opposite hue by ±30° and loses the buzz without losing the punch. Click the wheel to reseat the source.

35° source hue

Complementary (180°)

Maximum drama. Will vibrate at full chroma — desaturate one side.

Split-complementary (180° ± 30°)

Most illustrated UIs you admire use this. Same energy, no buzz.

Analogous (± 30°)

Warm & cozy, low contrast. Great for mood; add a value jump for hierarchy.

V. Gamut

What OKLCH reaches that sRGB can't.

OKLCH is a color-appearance space, not a display space. Ask for a chroma that falls outside sRGB and the value gets clamped to something representable. Each card shows the ideal (as the CSS engine would render it on a wide-gamut display) beside the nearest sRGB fallback we computed by clamping.

On Display-P3 hardware (Safari, Chrome 111+, Firefox 113+) you get ~25% more area. Wrap vivid overrides in @media (color-gamut: p3) and keep the sRGB hex as a fallback — progressive enhancement, zero cost to older devices.
VI. Pitfalls

Three quiet mistakes, three fixes.

No sliders here — just side-by-side. The left pane is the bad idea your eye “feels” as wrong even when it can't articulate why. The right pane is the considered version. Look for five seconds each; you'll feel the difference.

Avoid
Pure #000 on pure #fff. Halation — white bleeds into the edges, pupils contract, after ten minutes your eyes burn.
#000000 on #FFFFFF · cold, clinical, tiring
Prefer
Warm charcoal #2A2320 on cream #FAF4EE. Same legibility, zero buzz. Your retina relaxes.
#2A2320 on #FAF4EE · warm, calm, editorial
Avoid
Pure #0000ff body text. Blue wavelengths refract differently; your fovea cannot focus them sharply. Chromatic aberration.
#0000FF on #FFFFFF · aberrant, fuzzy at size
Prefer
Desaturated teal #1F7C78 on warm cream. Reads as cool and considered without the chromatic haze.
#1F7C78 on #FAF4EE · restful, brandable
Avoid
Eight-stop rainbow conic. Everything is “special” — so nothing is.
Conic 8-stop rainbow · AI-slop chaos
Prefer
Two-color gradient, pink to teal. One axis, one decision, one mood.
linear-gradient(135deg in oklab, pink, teal) · intention
Avoid
Default sRGB interpolation averages RGB channels and collapses through a muddy olive-taupe at 50%. Not a neutral — a dirty one.
linear-gradient(135deg, pink, teal) · sRGB default
Prefer
Four cleaner alternatives. Oklab gives a neutral midpoint without hue-rotating. Oklch short-arc runs through yellow-green; oklch longer hue runs through purples. Or place your own midpoint by hand.
pick the interpolation that matches the mood you want
sRGB defaultchannels averaged
in oklabclean neutral middle
in oklchthrough yellow & green
in oklch longer huethrough purples
hand-placed stopdesigner picks midpoint
VII. Reference

This page's tokens.

A designer's bookmark. Hex, OKLCH, role, and a WCAG-checked pairing for each. The page itself uses these values — if anything on this page passes your own contrast checker at 4.5:1, it's because of this table.

TokenHexOKLCHRolePairs with (AA)
--bg#FAF4EEoklch(0.96 0.014 75)Surface (60%)ink, blue-deep, pink-deep
--ink#2A2320oklch(0.24 0.012 40)Body copybg (13.5:1 AAA)
--muted#9A8A84oklch(0.62 0.016 35)Labels, captionsbg — large text only
--pink#E89B89oklch(0.74 0.11 35)Surfaces / cardsink (7.2:1 AAA)
--pink-deep#C96F5Coklch(0.62 0.14 32)Section headings, CTAbg (3.4:1 large)
--blue#3FBFB5oklch(0.72 0.11 188)Surfaces / cardsink (6.5:1 AA+)
--blue-deep#1F7C78oklch(0.51 0.09 190)Links, buttonsbg (4.8:1 AA)
--sun#F2C94Coklch(0.83 0.13 88)Rare accentink (8.1:1 AAA)
ai gen