CSS Color Module Evolution
Modern CSS provides native support for OKLAB/OKLCH color spaces and explicit gradient interpolation control.Copy
┌───────────────────────────────────────────────────────────────────────────────┐
│ CSS COLOR MODULE EVOLUTION │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ Level 1-3 (1996-2011): │
│ ===================== │
│ │
│ - rgb(), rgba() - sRGB only │
│ - hsl(), hsla() - sRGB, polar coordinates │
│ - Named colors (140 keywords) │
│ - #RRGGBB, #RGB hex notation │
│ │
│ Limitations: │
│ - All interpolation in sRGB (muddy middle) │
│ - No perceptually uniform alternatives │
│ - Limited color gamut (sRGB only) │
│ │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ Level 4 (2022-2024): │
│ =================== │
│ │
│ New Color Spaces: │
│ - lab(), lch() - CIE Lab perceptual space │
│ - oklab(), oklch() - Improved perceptual uniformity │
│ - color() function for arbitrary color spaces │
│ - display-p3, rec2020 wide gamut support │
│ │
│ New Interpolation: │
│ - color-mix(in <colorspace>, ...) function │
│ - Explicit interpolation space in gradients │
│ │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ Level 5 (2024-present): │
│ ====================== │
│ │
│ Additional Features: │
│ - Relative color syntax: oklch(from var(--base) l c h) │
│ - color-contrast() for accessibility │
│ - Custom color spaces via @color-profile │
│ │
└───────────────────────────────────────────────────────────────────────────────┘
Gradient Interpolation Syntax
The modern gradient syntax allows explicit specification of interpolation color space.Copy
┌───────────────────────────────────────────────────────────────────────────────┐
│ GRADIENT INTERPOLATION SYNTAX │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ Legacy Syntax (sRGB implicit): │
│ ============================== │
│ │
│ background: linear-gradient(to right, blue, yellow); │
│ /* Interpolates in sRGB - will have muddy middle */ │
│ │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ Modern Syntax (explicit color space): │
│ ===================================== │
│ │
│ background: linear-gradient(in oklab to right, blue, yellow); │
│ /* Interpolates in OKLAB - clean transition */ │
│ │
│ background: linear-gradient(in oklch to right, blue, yellow); │
│ /* Interpolates in OKLCH - may cross through more saturated colors */ │
│ │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ Full Syntax: │
│ ============ │
│ │
│ linear-gradient( │
│ [in <colorspace>]? // Optional: oklab, oklch, srgb, etc │
│ [<hue-interpolation-method>]? // Optional: shorter, longer, etc │
│ <direction>, // Required: to right, 45deg, etc │
│ <color-stop-list> // Required: colors and positions │
│ ) │
│ │
│ Examples: │
│ - linear-gradient(in oklab, red, blue) │
│ - linear-gradient(in oklch shorter hue, red, blue) │
│ - linear-gradient(in oklch longer hue to right, red, blue) │
│ │
└───────────────────────────────────────────────────────────────────────────────┘
Hue Interpolation Methods
When using OKLCH (polar coordinates), the hue can be interpolated in different directions around the color wheel.Copy
┌───────────────────────────────────────────────────────────────────────────────┐
│ HUE INTERPOLATION METHODS │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ Given: Red (H=30) to Blue (H=250) │
│ │
│ 0/360 │
│ │ │
│ Red * │
│ (30) / │
│ / │
│ / shorter = 140 degrees │
│ 270 ───────────────┼─────────────── 90 │
│ \ │
│ \ longer = 220 degrees (other way) │
│ \ │
│ Blue * │
│ (250) │
│ │ │
│ 180 │
│ │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ shorter hue (default): │
│ ====================== │
│ - Takes the smaller arc between hues │
│ - 30 -> 250: goes through 0/360 (passing through red, magenta) │
│ - Distance: 360 - 250 + 30 = 140 degrees │
│ │
│ longer hue: │
│ ============ │
│ - Takes the larger arc between hues │
│ - 30 -> 250: goes through 180 (passing through yellow, green, cyan) │
│ - Distance: 220 degrees │
│ │
│ increasing hue: │
│ =============== │
│ - Always increases hue value │
│ - 30 -> 250: 30 -> 250 directly (220 degrees) │
│ │
│ decreasing hue: │
│ =============== │
│ - Always decreases hue value │
│ - 30 -> 250: 30 -> 0/360 -> 250 (140 degrees, wrapping) │
│ │
└───────────────────────────────────────────────────────────────────────────────┘
Core-to-Core Gradient Patterns
When creating gradients between two Core Colors, you have three main options.Copy
┌───────────────────────────────────────────────────────────────────────────────┐
│ CORE-TO-CORE GRADIENT PATTERN │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ Base colors: Blue (H=230) to Green (H=130) │
│ Both at: L=55%, C=0.15 │
│ │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ Option 1: OKLAB (Cartesian, direct) │
│ ==================================== │
│ │
│ background: linear-gradient( │
│ in oklab to right, │
│ var(--oklch-primary), │
│ var(--oklch-success) │
│ ); │
│ │
│ Path: Blue(230) ----> Cyan(~180) ----> Green(130) │
│ Result: Direct line through color space, passes through cyan region │
│ │
│ ┌───────────┬───────────┬───────────┬───────────┐ │
│ │ BLUE │ teal │ CYAN │ GREEN │ │
│ │ vibrant │ clean │ (neutral) │ vibrant │ │
│ └───────────┴───────────┴───────────┴───────────┘ │
│ │
│ Note: Center may be slightly desaturated (geometric midpoint) │
│ But the transition feels CLEAN, not muddy │
│ │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ Option 2: OKLCH shorter hue │
│ =========================== │
│ │
│ background: linear-gradient( │
│ in oklch shorter hue to right, │
│ var(--oklch-primary), │
│ var(--oklch-success) │
│ ); │
│ │
│ Hue distance: 230 - 130 = 100 degrees (shorter = direct) │
│ Path: Blue(230) --> Teal(200) --> Cyan(180) --> Turquoise(150) --> Green │
│ │
│ ┌───────────┬───────────┬───────────┬───────────┐ │
│ │ BLUE │ TEAL │ CYAN │ GREEN │ │
│ │ C=0.15 │ C=0.15 │ C=0.15 │ C=0.15 │ │
│ └───────────┴───────────┴───────────┴───────────┘ │
│ │
│ Note: ALL intermediate colors have SAME CHROMA │
│ This is the most "equal saturation" result │
│ │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ Option 3: OKLCH longer hue │
│ ========================== │
│ │
│ background: linear-gradient( │
│ in oklch longer hue to right, │
│ var(--oklch-primary), │
│ var(--oklch-success) │
│ ); │
│ │
│ Hue distance: 360 - 100 = 260 degrees (the long way around) │
│ Path: Blue --> Purple --> Magenta --> Red --> Orange --> Yellow --> Green │
│ │
│ ┌───────┬───────┬───────┬───────┬───────┬───────┬───────┐ │
│ │ BLUE │PURPLE │ RED │ORANGE │YELLOW │ LIME │ GREEN │ │
│ └───────┴───────┴───────┴───────┴───────┴───────┴───────┘ │
│ │
│ Note: RAINBOW effect - passes through entire spectrum │
│ Use sparingly! Can be overwhelming. │
│ │
└───────────────────────────────────────────────────────────────────────────────┘
OKLCH vs OKLAB for color-mix
The choice between OKLCH and OKLAB incolor-mix() depends on the use case.
Copy
┌───────────────────────────────────────────────────────────────────────────────┐
│ OKLCH vs OKLAB FOR color-mix() │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ Use case: Lighten a color (hover state) │
│ ====================================== │
│ │
│ color-mix(in oklch, oklch(55% 0.15 230), white 15%) │
│ │
│ OKLCH interpolation: │
│ - L: 55% -> 100% (interpolated toward white's 100%) │
│ - C: 0.15 -> 0 (interpolated toward white's 0) │
│ - H: 230 -> undefined (white has no hue) │
│ │
│ Result: Lighter, slightly less saturated, same hue │
│ This is CORRECT for a hover state. │
│ │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ Use case: Add transparency (muted state) │
│ ======================================== │
│ │
│ color-mix(in oklab, oklch(55% 0.15 230), transparent 80%) │
│ │
│ OKLAB interpolation: │
│ - L, a, b interpolate linearly │
│ - Alpha becomes 0.2 │
│ │
│ OKLAB is preferred because: │
│ - More predictable behavior with transparency │
│ - Avoids hue discontinuities near zero chroma │
│ │
└───────────────────────────────────────────────────────────────────────────────┘
Browser Support
Copy
┌───────────────────────────────────────────────────────────────────────────────┐
│ BROWSER SUPPORT: color-mix() and gradients │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ As of December 2024: │
│ ==================== │
│ │
│ color-mix(): │
│ Chrome: 111+ (March 2023) │
│ Firefox: 113+ (May 2023) │
│ Safari: 16.2+ (December 2022) │
│ Edge: 111+ (March 2023) │
│ Global support: ~94% │
│ │
│ oklch() / oklab(): │
│ Chrome: 111+ (March 2023) │
│ Firefox: 113+ (May 2023) │
│ Safari: 15.4+ (March 2022) │
│ Edge: 111+ (March 2023) │
│ Global support: ~94% │
│ │
│ gradient interpolation (in <colorspace>): │
│ Chrome: 111+ (March 2023) │
│ Firefox: 121+ (December 2023) │
│ Safari: 16.2+ (December 2022) │
│ Edge: 111+ (March 2023) │
│ Global support: ~93% │
│ │
└───────────────────────────────────────────────────────────────────────────────┘
CSS Implementation Examples
Copy
/* Core Colors (OKLCH) */
:root {
--oklch-primary: oklch(55% 0.15 230);
--oklch-success: oklch(55% 0.15 130);
--oklch-danger: oklch(55% 0.18 25);
--oklch-warning: oklch(65% 0.15 85);
}
/* Flow: Hover states use OKLCH */
:root {
--oklch-primary-hover: color-mix(in oklch, var(--oklch-primary), white 15%);
--oklch-success-hover: color-mix(in oklch, var(--oklch-success), white 15%);
}
/* Flow: Transparency uses OKLAB */
:root {
--oklch-primary-muted: color-mix(in oklab, var(--oklch-primary), transparent 80%);
--oklch-success-muted: color-mix(in oklab, var(--oklch-success), transparent 80%);
}
/* Overlays always use OKLAB */
:root {
--color-overlay-light-1: color-mix(in oklab, var(--color-text-primary), transparent 92%);
--color-overlay-dark-1: color-mix(in oklab, black, transparent 95%);
}
/* Gradients between Core Colors */
.gradient-header {
background: linear-gradient(
in oklab to right,
var(--oklch-primary),
var(--oklch-success)
);
}
/* Fallback for older browsers */
.gradient-header {
background: linear-gradient(to right, #0066cc, #00aa55);
background: linear-gradient(
in oklab to right,
oklch(55% 0.15 230),
oklch(55% 0.15 130)
);
}
Decision Matrix
Copy
┌───────────────────────────────────────────────────────────────────────────────┐
│ WHEN TO USE WHICH COLOR SPACE │
├───────────────────────────────────────────────────────────────────────────────┤
│ │
│ Use OKLCH when: │
│ ================ │
│ - Defining identity colors (Core Colors) │
│ - Mixing with black/white (no hue change) │
│ - Creating color palettes with hue rotation │
│ - Need to preserve chroma during lightening │
│ │
│ Use OKLAB when: │
│ =============== │
│ - Mixing with transparency │
│ - Interpolating between two arbitrary colors │
│ - Creating gradients between different hues │
│ - Need predictable intermediate colors │
│ │
│ Use sRGB when: │
│ ============== │
│ - Legacy browser support required │
│ - Matching existing sRGB color values │
│ - Hex colors needed for non-CSS contexts │
│ │
└───────────────────────────────────────────────────────────────────────────────┘
THE CENTER
Gradients as Visual Flow Indicators
In the Core-Flow system, gradients represent the “flow” of information between data points. A gradient from Blue (primary) to Green (success) might indicate a transition from “in progress” to “complete.”Copy
Connection to Core-Flow:
├── Core colors are endpoints (OKLCH identity)
├── Gradient interpolation shows the path (OKLAB flow)
├── Clean gradients = clear progression indication
└── Using `in oklab` ensures perceptual smoothness
linear-gradient(in oklab, ...) is not a hack - it is the correct way to create perceptually clean color transitions that communicate data relationships effectively.
Syntax Spectrum
File-type specific syntax highlighting colors