Relative Color Syntax
In Unit 1 you used modern color functions and color-mix(). Relative color syntax (CSS Color Module Level 5) goes further: you start from any resolved color (including var(--token)) and compute a new color by reusing or adjusting its channels in a chosen color space.
That lets you derive whole palettes (lighter surfaces, darker borders, hover states) from one brand variable while staying perceptually consistent when you use spaces like oklch.
Official reference: MDN: CSS data type <color>, relative colors.
Core Idea
Instead of hand-picking six hex codes, you express:
“Take my brand color in OKLCH, reduce lightness a bit, keep chroma and hue”
or “Take surface color and bump lightness for a hover state.”
The browser resolves the origin color, then builds a new color in the target function using channel keywords.
Syntax Pattern
General shape:
oklch(from var(--brand) l c h);
oklch(from var(--brand) calc(l * 0.92) c h);
rgb(from var(--brand) r g b / 0.85);
-
Color space function: outer function sets the output space (
oklch,srgb,hsl, etc.). -
from <color>: the origin; can be a literal,currentColor, or a custom property that resolves to a color. -
Channels: use the channel names for that space (
lchfor OKLCH in the common form, orrgbforrgb(), possibly with alpha).
Lightness steps in oklch tend to look more even to human eyes than tweaking RGB hex by hand. For brand palettes, starting in OKLCH reduces “muddy” midtones.
Example: Brand Scale from One Variable
:root {
/* Base token designers can change */
--brand: oklch(0.55 0.18 250);
}
.surface {
/* Lighter background */
background: oklch(from var(--brand) calc(l + 0.4) calc(c * 0.15) h);
color: oklch(from var(--brand) calc(l * 0.35) c h);
}
.border {
border-color: oklch(from var(--brand) calc(l * 0.75) calc(c * 0.6) h);
}
.accent {
/* Saturated use of same hue family */
background: oklch(from var(--brand) 0.62 0.2 h);
}
Adjust the formulas to taste; the point is that all rows relate to --brand, so a single token change ripples through the system.
Relative Colors vs color-mix()
Both are valid; they solve slightly different problems:
-
color-mix(): “Blend A and B by a percentage in a space.” Great for overlays, mixing brand with white/black, or two theme tokens. - Relative colors: “Recompute channels from a single base.” Great for systematic ramps (surface, border, text on surface) tied to one seed color.
You can combine them: relative syntax to get a hover color, then color-mix() to blend that with a translucent scrim.
Browser Support and Fallbacks
Relative color syntax is relatively new; support improves every release. Before relying on it in production, check caniuse: CSS relative colors and your analytics.
Progressive enhancement pattern:
.btn {
/* Solid fallback */
background: #2563eb;
background: oklch(from var(--brand, #2563eb) calc(l * 0.95) c h);
}
Or define parallel custom properties in a build step or manual palette for older browsers, and override with relative colors inside @supports:
@supports (color: rgb(from red r g b)) {
.surface {
background: oklch(from var(--brand) calc(l + 0.35) calc(c * 0.2) h);
}
}
Demo: Live Swatches (Supporting Browsers)
If your browser supports relative colors, the boxes below derive from a single --demo-brand value. Change the variable in DevTools to see the row update together.
Non-supporting browsers may ignore these declarations; keep fallbacks for critical contrast (text on background).
Accessibility: Contrast Still Matters
Relative math does not guarantee WCAG contrast ratios. After deriving colors, run a checker (automated in CI or manually in DevTools) especially for text vs background. If a generated pair fails, nudge l or mix with a fixed neutral using color-mix().
Status and error states should not depend only on color; pair with iconography or text, as you learned in earlier units.
Summary
Relative color syntax lets you treat one or few seed tokens as inputs to a computable palette in modern color spaces. Pair it with color-mix(), custom properties, and @supports fallbacks to build theme systems that are flexible in evergreen browsers and still usable elsewhere.