CSS Colors: From Classic to Modern

What You'll Learn

  • Classic color formats: named colors, hex, RGB, and HSL
  • Modern color syntax with space-separated values
  • OKLCH: the modern perceptually uniform color space
  • color-mix() for dynamic color blending
  • Gradient interpolation in different color spaces
  • Special color keywords: currentColor and transparent
  • Creating consistent color palettes with OKLCH
  • Best practices for choosing color formats

Introduction to CSS Colors

CSS color capabilities have evolved dramatically over the years. What started with 147 named colors and basic hex values has grown into a sophisticated system with perceptually uniform color spaces, dynamic color mixing, and interpolation control.

Modern CSS colors go beyond aesthetics—they enable accessible design systems, vibrant gradients, and precise color control that matches how humans actually perceive color.

Named Colors and Hexadecimal

The classics: 147 named colors and hexadecimal notation. These remain the most common color formats due to their simplicity and wide support.

Named Colors

CSS defines 147 named colors from red to rebeccapurple. While convenient for quick prototyping, they offer limited control and aren't suitable for precise design systems.

Hexadecimal Format

Hex colors use the format #RRGGBB where each pair represents red, green, and blue values in hexadecimal (00-FF). You can also use 3-digit shorthand (#RGB) and 8-digit format with alpha (#RRGGBBAA).

/* Named colors */ color: red; color: dodgerblue; color: rebeccapurple; /* Hexadecimal */ color: #2196f3; /* 6-digit */ color: #f90; /* 3-digit shorthand (#ff9900) */ color: #2196f380; /* 8-digit with 50% alpha */

RGB and HSL

RGB (Red, Green, Blue) and HSL (Hue, Saturation, Lightness) are the workhorses of CSS colors. They come in both classic comma-separated syntax and modern space-separated syntax.

Classic Syntax (Comma-Separated)

color: rgb(33, 150, 243); color: rgba(33, 150, 243, 0.5); color: hsl(207, 90%, 54%); color: hsla(207, 90%, 54%, 0.5);

Modern Syntax (Space-Separated)

The modern syntax eliminates commas and uses / for alpha, making it more consistent with newer color functions.

color: rgb(255 87 34); color: rgb(255 87 34 / 0.5); /* Alpha with / */ color: hsl(14 100% 57%); color: hsl(14 100% 57% / 0.5);

OKLCH - The Modern Choice

OKLCH (OK Lightness, Chroma, Hue) is a perceptually uniform color space, meaning equal numeric changes produce equal visual changes. This is revolutionary for creating consistent color palettes and gradients.

Why Perceptual Uniformity Matters

In HSL, hsl(120 50% 50%) (green) appears much brighter than hsl(240 50% 50%) (blue) even though they have the same lightness value. OKLCH fixes this—colors with the same lightness actually look equally bright.

/* OKLCH format: oklch(Lightness% Chroma Hue) */ color: oklch(70% 0.2 140); /* Green */ color: oklch(70% 0.2 220); /* Blue */ color: oklch(70% 0.2 320); /* Magenta */ /* All three appear equally bright! */ /* With alpha */ color: oklch(70% 0.2 220 / 0.5); /* OKLAB (rectangular coordinates) */ color: oklab(60% 0.15 0.1);

color-mix() - Dynamic Color Blending

The color-mix() function lets you blend two colors together in a specified color space, without needing a preprocessor. It's perfect for creating hover states, tints, shades, and dynamic color variations.

/* Mix in sRGB (default) */ color: color-mix(in srgb, red 50%, blue); /* Mix in OKLCH (more vibrant) */ color: color-mix(in oklch, red 50%, blue); /* Create tints/shades */ background: color-mix(in oklch, var(--primary) 20%, white); /* Tint */ background: color-mix(in oklch, var(--primary) 80%, black); /* Shade */ /* Hover state */ .button { background: var(--primary); } .button:hover { background: color-mix(in oklch, var(--primary) 85%, white); }

Gradient Interpolation

Modern CSS allows you to specify which color space to use when interpolating gradients. This has a dramatic impact on the visual result.

The Problem with sRGB

By default, gradients interpolate in sRGB, which often creates muddy, grayish midpoints between vibrant colors. This happens because sRGB isn't perceptually uniform.

The Solution: OKLCH Interpolation

Interpolating in OKLCH maintains saturation throughout the gradient, producing vibrant, appealing transitions.

/* Old way - muddy middle colors */ background: linear-gradient(to right, red, blue); background: linear-gradient(to right in srgb, red, blue); /* Modern way - vibrant throughout! */ background: linear-gradient(to right in oklch, red, blue); background: linear-gradient(to right in oklch, yellow, blue); /* Also works with radial and conic gradients */ background: radial-gradient(in oklch, #ff0000, #0000ff); background: conic-gradient(in oklch, red, yellow, green, blue, red);

Special Color Keywords

CSS provides special keywords that enable dynamic, context-aware coloring without hard-coding values.

currentColor

The currentColor keyword references the current value of the color property. It's perfect for icons, borders, and decorative elements that should match the text color.

.icon-box { color: #e91e63; border: 3px solid currentColor; /* Matches text color */ } .icon-box svg { fill: currentColor; /* Icon matches text color */ } /* Hover example */ .link { color: blue; text-decoration-color: currentColor; } .link:hover { color: red; /* text-decoration automatically updates! */ }

transparent

The transparent keyword is equivalent to rgba(0, 0, 0, 0). It's commonly used for removing backgrounds or creating fade-to-nothing effects.

/* Remove background */ background: transparent; /* Fade to transparent gradient */ background: linear-gradient(to right, red, rgba(255, 0, 0, 0.5), transparent ); /* Border trick for triangles */ border-top: 20px solid transparent; border-bottom: 20px solid transparent; border-left: 30px solid blue;

Creating Color Palettes with OKLCH

One of OKLCH's superpowers is generating entire color palettes programmatically. By keeping chroma and hue constant while varying lightness, you create visually consistent scales.

:root { --base-hue: 200; /* Change this to generate different palettes! */ } .color-50 { background: oklch(95% 0.05 var(--base-hue)); } .color-100 { background: oklch(90% 0.08 var(--base-hue)); } .color-200 { background: oklch(80% 0.12 var(--base-hue)); } .color-300 { background: oklch(70% 0.15 var(--base-hue)); } .color-400 { background: oklch(60% 0.18 var(--base-hue)); } .color-500 { background: oklch(50% 0.20 var(--base-hue)); } .color-600 { background: oklch(40% 0.18 var(--base-hue)); } .color-700 { background: oklch(30% 0.15 var(--base-hue)); } .color-800 { background: oklch(20% 0.10 var(--base-hue)); } .color-900 { background: oklch(15% 0.05 var(--base-hue)); } /* Change --base-hue to 140 for green, 280 for purple, 40 for orange! */

Best Practices

With so many color formats available, how do you choose? Here's a decision guide:

For Design Systems: OKLCH

Use oklch() for creating color palettes and scales. Its perceptual uniformity makes it trivial to generate consistent colors.

For Gradients: in oklch

Add in oklch to your gradients to eliminate muddy midpoints and maintain vibrant colors throughout the transition.

For Color Mixing: color-mix()

Use color-mix(in oklch, ...) for tints, shades, and hover states. No preprocessor needed, and the results are more perceptually pleasing.

For Transparency: Modern Syntax

Use the modern syntax with / for alpha values. It works with all color formats and is more consistent than separate rgba/hsla functions.

For Theme Variables: CSS Custom Properties

Store colors in custom properties for easy theme switching. Use semantic names like --primary-color rather than --blue.

For Icons and Borders: currentColor

Use currentColor for elements that should inherit the text color, enabling automatic color coordination.

For Quick Prototyping: Hex or Named Colors

Hex codes and named colors are fine for rapid prototyping, but plan to upgrade to OKLCH for production design systems.

Key Takeaways