OKLAB Cartesian Coordinate System
OKLAB is a Cartesian coordinate system for color representation, fundamentally different from the polar coordinate system used by OKLCH. While OKLCH uses Lightness (L), Chroma (C), and Hue (H) in a cylindrical arrangement, OKLAB uses three perpendicular axes.Copy
┌──────────────────────────────────────────────────────────────────────────────┐
│ OKLAB CARTESIAN COORDINATE SYSTEM │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ +b (Yellow) │
│ │ │
│ │ │
│ │ │
│ │ * (L, a, b) │
│ │ / │
│ │ / │
│ │ / │
│ -a ─────────────────┼───────────────── +a │
│ (Green) │ \ (Red/Magenta) │
│ │ \ │
│ │ \ │
│ │ * Second Color │
│ │ │
│ -b (Blue) │
│ │
│ L axis: Perpendicular to this plane (0 = black, 1 = white) │
│ a axis: Green (-) to Red/Magenta (+) │
│ b axis: Blue (-) to Yellow (+) │
│ │
│ KEY PROPERTY: Straight line between two colors = perceptually uniform path │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
Mathematical Relationship: OKLAB to OKLCH
The conversion between OKLAB (Cartesian) and OKLCH (Polar) follows standard coordinate transformation equations:Copy
┌──────────────────────────────────────────────────────────────────────────────┐
│ COORDINATE CONVERSION FORMULAS │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ OKLAB (Cartesian) → OKLCH (Polar): │
│ ────────────────────────────────── │
│ C = √(a² + b²) // Chroma = distance from center │
│ H = atan2(b, a) × 180/π // Hue = angle in a,b plane │
│ if H < 0: H += 360 // Normalize to [0, 360) │
│ │
│ OKLCH (Polar) → OKLAB (Cartesian): │
│ ────────────────────────────────── │
│ a = C × cos(H × π/180) // a-axis projection │
│ b = C × sin(H × π/180) // b-axis projection │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
Copy
// Cartesian to Polar conversion
// Chroma = distance from center (magnitude of a,b vector)
const c = Math.sqrt(a * a + b * b);
// Hue = angle in a,b plane (converted to degrees)
let h = (Math.atan2(b, a) * 180) / Math.PI;
if (h < 0) h += 360;
Copy
// Polar to Cartesian conversion
// a = C * cos(H)
const a = oklch.c * Math.cos((oklch.h * Math.PI) / 180);
// b = C * sin(H)
const b = oklch.c * Math.sin((oklch.h * Math.PI) / 180);
Coordinate System Comparison
Copy
┌──────────────────────────────────────────────────────────────────────────────┐
│ COORDINATE SYSTEM COMPARISON: OKLCH vs OKLAB │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ OKLCH (Polar/Cylindrical) OKLAB (Cartesian/Rectangular) │
│ ═════════════════════════ ═════════════════════════════ │
│ │
│ L (Lightness) L (Lightness) │
│ │ │ │
│ │ H (Hue) │ +b │
│ │ / \ │ │ │
│ │ / \ │ │ │
│ │ / \ -a ──┼───┼───+a │
│ │ \ │ │ │
│ ┼──────────\ │ -b │
│ │ C \ │ │
│ │ (radius) \ │
│ │
│ Intuitive for: Optimal for: │
│ • Human color selection • Linear interpolation │
│ • Hue rotation • Color mixing │
│ • Identity definition • Transparency blending │
│ • Golden angle distribution • Gradient generation │
│ │
│ CSS: oklch(L% C H) CSS: oklab(L% a b) │
│ Example: oklch(55% 0.15 230) Example: oklab(55% -0.12 -0.09) │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
The color-mix() Function
Thecolor-mix() function was introduced in CSS Color Level 5 to enable programmatic color blending directly in CSS.
Copy
color-mix(in <color-space>, <color1> [<percentage>], <color2> [<percentage>])
<color-space>: The interpolation color space (srgb, lab, oklab, oklch, etc.)<color1>,<color2>: The colors to blend<percentage>: Optional weighting (default 50% each)
Why OKLAB for color-mix()?
Copy
/* Using color-mix() for perceptually accurate transparency (in oklab color space) */
/* Light overlays (using color-mix in oklab) */
--color-overlay-light-1: color-mix(in oklab, var(--color-text-primary), transparent 92%);
--color-overlay-light-2: color-mix(in oklab, var(--color-text-primary), transparent 88%);
--color-overlay-light-3: color-mix(in oklab, var(--color-text-primary), transparent 84%);
--color-overlay-light-4: color-mix(in oklab, var(--color-text-primary), transparent 80%);
/* Dark overlays (using color-mix in oklab) */
--color-overlay-dark-1: color-mix(in oklab, black, transparent 95%);
--color-overlay-dark-2: color-mix(in oklab, black, transparent 90%);
--color-overlay-dark-3: color-mix(in oklab, black, transparent 80%);
--color-overlay-dark-4: color-mix(in oklab, black, transparent 70%);
Interpolation Behavior: OKLAB vs sRGB
Copy
┌──────────────────────────────────────────────────────────────────────────────┐
│ INTERPOLATION PATH COMPARISON │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ Example: Mixing Blue (oklch 55% 0.15 230) with Orange (oklch 55% 0.15 50) │
│ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ sRGB Interpolation │ │
│ │ (NON-perceptually uniform) │ │
│ │ │ │
│ │ Blue ──────────► MUDDY ◄────────── Orange │ │
│ │ #2380c7 BROWN #b58900 │ │
│ │ #7d7064 │ │
│ │ │ │
│ │ Path curves through low-saturation zone = "muddy" appearance │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ OKLAB Interpolation │ │
│ │ (Perceptually uniform) │ │
│ │ │ │
│ │ Blue ──────────► CLEAN ◄────────── Orange │ │
│ │ #2380c7 PURPLE #b58900 │ │
│ │ #8b6aa0 │ │
│ │ │ │
│ │ Straight line through color space = maintains saturation │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
│ KEY INSIGHT: OKLAB's straight-line interpolation preserves perceived │
│ chroma throughout the transition, preventing "muddy" intermediate colors. │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
color-mix() Pattern Taxonomy
Copy
┌──────────────────────────────────────────────────────────────────────────────┐
│ COLOR-MIX() PATTERN TAXONOMY │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ PATTERN A: Transparency Derivation │
│ ══════════════════════════════════ │
│ │
│ color-mix(in oklab, <CORE-COLOR>, transparent <N%>) │
│ │
│ Purpose: Create semi-transparent versions of Core Colors │
│ Usage: Backgrounds, selections, overlays │
│ │
│ ────────────────────────────────────────────────────────────────────────── │
│ │
│ PATTERN B: Lightness Adjustment │
│ ═══════════════════════════════ │
│ │
│ color-mix(in oklch, <CORE-COLOR>, white <N%>) │
│ color-mix(in oklab, <CORE-COLOR>, black <N%>) │
│ │
│ Purpose: Create lighter/darker variants of Core Colors │
│ Usage: Hover states, active states, shadows │
│ │
│ ────────────────────────────────────────────────────────────────────────── │
│ │
│ PATTERN C: Saturation Reduction │
│ ═══════════════════════════════ │
│ │
│ color-mix(in oklab, <CORE-COLOR>, gray <N%>) │
│ │
│ Purpose: Desaturate colors (reduce chroma) │
│ Usage: Disabled states, muted variants, comments │
│ │
│ ────────────────────────────────────────────────────────────────────────── │
│ │
│ PATTERN D: Color Blending │
│ ═════════════════════════ │
│ │
│ color-mix(in oklab, <CORE-COLOR-A>, <CORE-COLOR-B> <N%>) │
│ │
│ Purpose: Blend two Core Colors together │
│ Usage: Merge lines, gradients, state transitions │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
Implementation Examples
Copy
/* Derived colors using color-mix() */
--oklch-primary-hover: color-mix(in oklch, var(--oklch-primary), white 15%);
--oklch-primary-muted: color-mix(in oklab, var(--oklch-primary), transparent 80%);
--oklch-success-hover: color-mix(in oklch, var(--oklch-success), white 15%);
--oklch-success-muted: color-mix(in oklab, var(--oklch-success), transparent 80%);
--oklch-danger-hover: color-mix(in oklch, var(--oklch-danger), white 15%);
--oklch-danger-muted: color-mix(in oklab, var(--oklch-danger), transparent 80%);
--oklch-warning-hover: color-mix(in oklch, var(--oklch-warning), white 10%);
--oklch-warning-muted: color-mix(in oklab, var(--oklch-warning), transparent 80%);
- Hover states: Use
oklchfor hue preservation during lightening - Muted states: Use
oklabfor perceptually uniform transparency
TypeScript color-mix() Utilities
Copy
┌──────────────────────────────────────────────────────────────────────────────┐
│ COLOR-MIX UTILITY FUNCTIONS │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ colorMix(color1, color2, space, percent) │
│ └──► "color-mix(in {space}, {color1} {percent}%, {color2})" │
│ │
│ colorTransparent(color, opacity, space) │
│ └──► "color-mix(in {space}, {color}, transparent {100-opacity}%)" │
│ │
│ colorDarker(color, amount=30) │
│ └──► colorMix(color, "black", "oklab", 100-amount) │
│ │
│ colorLighter(color, amount=30) │
│ └──► colorMix(color, "white", "oklab", 100-amount) │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
Copy
/**
* Generate CSS color-mix() string
* @param color1 First color (CSS value or variable)
* @param color2 Second color (CSS value or variable)
* @param space Color space for mixing
* @param percent Percentage of color1 (0-100)
*/
export function colorMix(
color1: string,
color2: string,
space: ColorMixSpace = "oklch",
percent: number = 50
): string {
return `color-mix(in ${space}, ${color1} ${percent}%, ${color2})`;
}
/**
* Generate transparent variant using color-mix
* @param color CSS color value or variable
* @param opacity Opacity 0-100
* @param space Color space (oklab recommended for transparency)
*/
export function colorTransparent(
color: string,
opacity: number,
space: ColorMixSpace = "oklab"
): string {
return `color-mix(in ${space}, ${color}, transparent ${100 - opacity}%)`;
}
/**
* Generate darker variant using color-mix
* Uses oklab for perceptually uniform darkening
*/
export function colorDarker(color: string, amount: number = 30): string {
return colorMix(color, "black", "oklab", 100 - amount);
}
/**
* Generate lighter variant using color-mix
* Uses oklab for perceptually uniform lightening
*/
export function colorLighter(color: string, amount: number = 30): string {
return colorMix(color, "white", "oklab", 100 - amount);
}
Color Space Decision Matrix
Copy
┌──────────────────────────────────────────────────────────────────────────────┐
│ COLOR-MIX() COLOR SPACE DECISION MATRIX │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ Operation Recommended Space Reason │
│ ═══════════════════════════ ═════════════════ ════════════════════ │
│ │
│ Mix with white (lighten) oklch Preserves hue │
│ Mix with black (darken) oklab Uniform darkening │
│ Mix with transparent oklab Perceptual opacity │
│ Mix with gray (desaturate) oklab Even chroma reduction │
│ Mix two Core Colors oklab No muddy midpoints │
│ Create hover state oklch Identity preservation │
│ Create active state oklab Subtle darkening │
│ Create disabled state oklab Desaturation │
│ Create selection background oklab Clean transparency │
│ Create gradient oklab Smooth transition │
│ │
│ DEFAULT RULE: When in doubt, use oklab │
│ EXCEPTION: When preserving hue is critical, use oklch │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
THE CENTER
How This Component Helps Visualize Information Flow
Copy
Connection to Core-Flow:
├── Flow colors derive from Core using color-mix()
├── OKLAB ensures perceptually uniform transitions
├── No "muddy" colors when flowing between states
└── Information intensity mapped to opacity levels
Transparency Patterns
Learn how transparency creates visual hierarchy through opacity gradients