The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| Use modern RGB syntax | .swatch { color: rgb(34 70 160 / 85%); } | View examples |
| Use cylindrical sRGB | .swatch { color: hsl(220 65% 42% / .85); } | View examples |
| Use perceptual polar color | .swatch { color: oklch(62% .18 255); } | View examples |
| Supply a Display P3 color | .swatch {
color: #0b63ce;
color: color(display-p3 0.05 0.38 0.86);
} | View examples |
| Gate wide-gamut syntax | @supports (color: color(display-p3 1 0 0)) {
.brand { color: color(display-p3 .1 .5 .9); };
} | View examples |
| Check output gamut | @media (color-gamut: p3) {
.hero { --accent: color(display-p3 .1 .55 .9); };
} | View examples |
| Mix in OKLab | .border {
color:
color-mix(in oklab, var(--accent) 35%, transparent);
} | View examples |
| Create a tint | .panel {
background:
color-mix(in oklch, var(--accent) 15%, white);
} | View examples |
| Choose a hue path | .spectrum {
color: color-mix(in oklch longer hue, red, blue);
} | View examples |
| Adjust an origin color | .hover {
color: oklch(from var(--accent) calc(l + .08) c h);
} | View examples |
| Change only alpha | .overlay {
background: rgb(from var(--accent) r g b / 35%);
} | View examples |
| Preserve a missing component | .variant { color: oklch(from var(--accent) l c none); } | View examples |
| Choose by used color scheme | :root {
color-scheme: light dark;
--surface: light-dark(#fff, #111827);
} | View examples |
| Adapt to forced colors | .button {
color: ButtonText;
background: ButtonFace;
border-color: ButtonText;
} | View examples |
| Remove decorative imagery | @media (forced-colors: active) {
.badge { background-image:
none; border: 1px solid CanvasText; };
} | View examples |
| Interpolate a gradient in OKLab | .hero {
background:
linear-gradient(in oklab, #06b6d4, #7c3aed);
} | View examples |
| Choose a gradient hue path | .wheel {
background:
linear-gradient(in oklch longer hue, red, blue);
} | View examples |
| Disable nonessential color motion | @media (prefers-reduced-motion: reduce) {
.pulse { animation: none; };
} | View examples |
| Pair color with a text cue | <p class="status" role="status"><span aria-hidden="true">✓</span> Backup complete</p> | View examples |
Modern CSS can express colors in device-independent and wide-gamut spaces, derive variants from tokens, and choose interpolation rules explicitly. Those powers do not guarantee contrast, gamut availability, or uniform browser support. This guide builds a resilient sRGB baseline, progressively enhances perceptual color, and separates computed color math from accessibility decisions.
Step by step
Detailed examples
Choose a space for the task
rgb() and hsl() produce sRGB colors; Lab and LCH are based on CIE Lab, while OKLab and OKLCH provide a perceptual model tuned for screen work. Polar spaces use hue angles and chroma; rectangular spaces use opponent axes. Equal numeric changes are not guarantees of equal contrast. Alpha controls compositing, not lightness, and transparent colors reveal whatever is behind them.
:root { --accent: #2764d8; --accent: oklch(58% .19 255); }
.button { background: var(--accent); color: white; } Keep an sRGB baseline before wide gamut
color() can address predefined spaces such as display-p3, rec2020, and srgb-linear. A syntactically valid color may still be outside the output device gamut and is gamut-mapped for display. Put an sRGB declaration first, optionally gate the enhancement with @supports, and use color-gamut only when device capability should influence the design. Screenshots and automated visual tests may run in sRGB even when production hardware is wider.
.brand { color: rgb(0 101 210); }
@supports (color: color(display-p3 0 0 0)) {
.brand { color: color(display-p3 0 .39 .88); }
} Specify the interpolation space explicitly
color-mix() combines colors in a declared interpolation method; the default space is OKLab when omitted, but explicit is clearer. Percentages are normalized under defined rules, and a total below 100% introduces alpha. Polar spaces accept shorter, longer, increasing, or decreasing hue interpolation. Mixing with transparent can change color channels differently from merely changing alpha, so inspect the composited result on actual surfaces.
:root {
--accent: oklch(58% .19 255);
--accent-soft: color-mix(in oklch, var(--accent) 18%, white);
--accent-border: color-mix(in oklab, var(--accent) 45%, transparent);
} Derive variants from an origin without string tricks
Relative color syntax begins with from <color>, converts the origin into the target function space, and exposes numeric channel keywords plus alpha. Components can be copied or used inside math. They are numbers in defined scales, so percentage assumptions do not transfer magically between channels. Provide precomputed fallback tokens because support and serialization vary, and clamp deliberate calculations when values could exceed a useful design range.
.link { --hover: #1746a2; --hover: oklch(from var(--accent) clamp(0, calc(l - .1), 1) c h); }
.link:hover { color: var(--hover); } Negotiate schemes without promising contrast
light-dark() chooses according to the used color scheme and requires the page or element to declare supported schemes with color-scheme. System colors adapt to forced palettes. Neither light-dark(), OKLCH lightness, color-mix(), nor contrast-color() by itself proves WCAG contrast for arbitrary text sizes and backgrounds. Validate final composited pairs, including hover, focus, disabled, visited, dark, high-contrast, and translucent states. Avoid forced-color-adjust:none unless you fully replace the lost adaptation.
:root { color-scheme: light dark; --surface: light-dark(#fff,#111827); --text: light-dark(#172033,#f8fafc); }
.card { color: var(--text); background: var(--surface); border: 1px solid currentColor; } Control interpolation and motion independently
Gradients accept an interpolation color space and optional hue method, while color-mix() carries its own explicit interpolation method. Transitions and animations otherwise use the interpolation rules defined for the animated property. Different spaces can produce visibly different midpoints and gamut excursions. Color changes still can flash or distract; respect prefers-reduced-motion for nonessential repeating effects, avoid rapid luminance changes, and animate paint-heavy full-screen gradients sparingly.
.hero { background: linear-gradient(90deg in oklab,#06b6d4,#7c3aed); animation: hue 6s alternate infinite; }
@keyframes hue { to { filter: hue-rotate(25deg); } }
@media (prefers-reduced-motion: reduce) { .hero { animation: none; } } Test computed colors on real output conditions
Cascade a conservative value before every new syntax whose absence would hide content. @supports detects parsing, not gamut, contrast, or correct appearance. Test sRGB and P3 displays, light and dark schemes, forced colors, increased contrast, print, transparency over varied surfaces, and disabled custom properties. Avoid encoding status by color alone; pair it with text, icons, patterns, or shape. Use currentColor to keep borders and icons synchronized with accessible foreground choices.
<p class="status" role="status"><span aria-hidden="true">✓</span> Backup complete</p> .status { color: #176b3a; color: oklch(47% .13 150); border: 2px solid currentColor; border-radius: .5rem; padding: .75rem; } Local code tester
Compare color interpolation spaces
Choose sRGB, OKLab, or OKLCH to compare gradient midpoints and derived surfaces while text and borders retain explicit fallbacks.
Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



