A Complete Guide to Modern CSS Colors

Master CSS Color Level 4 features including oklch, lab(), relative colors, light-dark() theming, and gradient improvements for vibrant web design.

CSS has undergone a significant evolution in how colors are defined and manipulated. Modern CSS color features, introduced in CSS Color Module Level 4 and expanded in subsequent updates, give developers unprecedented control over color on the web. From wider color gamuts that display more vibrant colors to new functions that make color manipulation intuitive, understanding these modern features is essential for creating visually stunning websites.

This comprehensive guide explores everything you need to know about modern CSS colors, from syntax updates that simplify your code to advanced color spaces that unlock new creative possibilities. Whether you're building a brand-new project or modernizing an existing site, mastering these color features will elevate your web development capabilities and help you create more engaging user experiences.

What You'll Learn

Key concepts covered in this guide

Modern Syntax Evolution

Space-separated values, slash for alpha channels, and simplified color declarations

Advanced Color Spaces

oklch, lab, lch, oklab, and hwb functions for precise color control

Relative Colors

Create dynamic color variations from existing base colors

Light-Dark Theming

Simplified light and dark mode implementation with light-dark()

Gradient Improvements

Color interpolation in different spaces for vibrant gradients

Browser Support

Current browser compatibility and progressive enhancement strategies

Understanding Color Spaces

Before diving into specific functions and syntax, it's important to understand what color spaces are and why they matter for web design.

What Is a Color Space?

A color space is a mathematical model that defines how colors are represented and displayed. Think of it as a coordinate system where each point represents a specific color. Different color spaces use different methods to organize and describe colors, which affects the range (gamut) of colors they can represent and how colors relate to each other. The traditional web has relied primarily on the sRGB color space, which was developed in the early days of computing and television. While sRGB works reliably across all devices, it has a relatively limited gamut compared to what modern displays can show.

The Evolution from sRGB to Wide Gamut

Modern displays can show significantly more colors than sRGB can represent. These displays support wider color gamuts like Display P3, which offers approximately 50% more colors than sRGB. CSS Color Module Level 4 introduced support for these wider gamuts, allowing developers to specify colors that weren't previously possible on the web. Wide gamut colors are particularly valuable for brand colors, photographs, and any situation where color accuracy and vibrancy matter. When you use a color defined in a wide gamut space on a compatible display, the result is noticeably more saturated and accurate than the same color rendered in sRGB.

Modern Color Syntax

The way we write CSS colors has evolved significantly. These changes make color definitions more consistent, easier to read, and more powerful when combined with newer features.

The Space-Separated Syntax

In the past, CSS color functions required commas to separate values. Modern CSS allows you to omit the commas and use spaces instead:

/* Modern space-separated syntax */
.element {
 color: rgb(255 0 0);
 background-color: hsl(0 100% 50%);
}

This syntax is required for newer color functions and makes it easier to work with relative colors and color manipulation. The space-separated approach is not just a cosmetic change--it creates consistency across all color functions and enables more intuitive color manipulation.

Alpha Channels Without the "a"

Previously, to add transparency to a color, you needed to use rgba() or hsla(). Modern CSS eliminates this distinction using slash notation:

/* Modern approach with slash notation */
.button {
 background-color: rgb(255 0 0 / 0.5);
 color: hsl(0 100% 50% / 0.8);
}

The slash notation works consistently across all color functions, whether you're using rgb(), hsl(), or the newer functions like lch() and oklch(). This consistency makes your code more predictable and easier to maintain.

Optional Units in hsl()

When using the space-separated syntax with hsl(), units are optional for the hue value:

/* Both are valid in modern CSS */
.red-1 { color: hsl(0deg 100% 50%); }
.red-2 { color: hsl(0 100% 50%); }

However, including the deg unit and percentage symbols is recommended because code editors like VS Code can display color swatch previews only when these units are present. This visual feedback helps you verify your color choices at a glance.

Modern CSS Color Syntax Examples
1/* Legacy syntax (still works but not recommended) */2.element {3 color: rgb(255, 0, 0);4 background-color: rgba(255, 0, 0, 0.5);5}6 7/* Modern space-separated syntax */8.element {9 color: rgb(255 0 0);10 background-color: rgb(255 0 0 / 0.5);11}12 13/* hsl() with modern syntax */14.heading {15 color: hsl(217 73% 50%);16}17 18.heading-with-alpha {19 color: hsl(217 73% 50% / 0.8);20}

Advanced Color Functions

Beyond the updates to rgb() and hsl(), CSS Color Module Level 4 introduced several new color functions that open up exciting possibilities for web design.

lab() and lch() for Perceptually Uniform Colors

The CIELAB color space (lab()) and its polar variant LCH (lch()) represent a major advancement in web color. These color spaces are designed to be perceptually uniform, meaning that the mathematical distance between two colors corresponds closely to how humans perceive the visual difference. In traditional sRGB, a small numerical change in one color channel might result in a barely noticeable color shift, while the same numerical change in another channel might produce a dramatic difference. With lab() and lch(), this inconsistency is minimized, making it easier to create smooth color gradients and consistent color scales.

/* lab() uses lightness, a-axis, and b-axis */
.brand-primary {
 color: lab(50% 50 0);
}

/* lch() uses lightness, chroma, and hue */
.vibrant-color {
 color: lch(50% 80 30deg);
}

The lch() function is particularly intuitive because it separates color into three properties that match how humans think about color: how light or dark it is (lightness), how vivid or muted it is (chroma), and where it sits on the color wheel (hue). This makes it easy to create color variations by adjusting just the property you want to change.

oklab() and oklch() for Better Performance

While lab() and lch() are perceptually uniform, the oklab() and oklch() color spaces (where "ok" stands for "optimized kappa") offer the same benefits with better computational performance. These functions were specifically designed to address some of the practical limitations of lab() and lch() while maintaining their perceptual uniformity advantages.

/* oklab() - optimized CIELAB */
.smooth-gradient {
 background: oklab(60% 0.1 -0.1);
}

/* oklch() - optimized LCH */
.predictable-color {
 color: oklch(70% 0.15 200deg);
}

For most web development purposes, oklch() has become the preferred choice for complex color manipulations because it combines the intuitive nature of LCH with the performance characteristics needed for production websites.

hwb() for Hue-Whiteness-Blackness

The hwb() (hue, whiteness, blackness) function provides another way to specify colors that some developers find more intuitive than hsl(). Instead of saturation and lightness, hwb() lets you specify how much white and black to mix into a pure hue. This function is particularly useful when you want to create tints (adding white) or shades (adding black) of a specific hue, as the relationship between the values and the resulting color is more straightforward than with hsl().

/* hwb() syntax: hue, whiteness %, blackness % */
.pastel {
 color: hwb(180 50% 10%);
}

.saturated {
 color: hwb(270 0% 20%);
}

color() for Predefined Color Spaces

The color() function allows you to specify colors in various predefined color spaces, including Display P3, Rec.2020, and sRGB. This function is essential when you need precise control over which color space is used, particularly for wide gamut displays. When you specify a color in a wide gamut space like display-p3 on a device that doesn't support it, the browser will automatically fall back to sRGB.

/* Using the color() function */
.p3-color {
 color: color(display-p3 0 1 0);
}

.rec2020-color {
 background-color: color(rec2020 0.5 0 0.8);
}

/* sRGB via color() function */
.standard-color {
 color: color(srgb 0.5 0.5 0.5);
}
Advanced Color Function Examples
1/* CIELAB color space */2.primary-color {3 color: lab(45% 55 -10);4}5 6/* LCH - more intuitive than RGB */7.vibrant-brand {8 color: lch(55% 90 320deg);9}10 11/* OKLCH - recommended for most use cases */12.smooth-color {13 color: oklch(65% 0.18 250deg);14}15 16/* HWB - hue, whiteness, blackness */17.tinted-white {18 background: hwb(200 60% 10%);19}20 21/* Wide gamut Display P3 */22.wide-gamut {23 color: color(display-p3 0.8 0.2 0.9);24}

Relative Colors for Dynamic Manipulation

One of the most powerful features of modern CSS is relative colors, which allow you to create new colors based on existing ones. This capability fundamentally changes how you can approach theming and color variations in your stylesheets.

The Basics of Relative Color Syntax

Relative colors use the from keyword to indicate the source color and channel keywords to reference the original color's values. The syntax might seem unusual at first, but it quickly becomes natural:

/* Create a semi-transparent version of a base color */
.primary-background {
 background-color: hsl(from var(--brand-color) h s l / 0.75);
}

In this example, the h, s, and l keywords represent the hue, saturation, and lightness values from --brand-color. By keeping the same hue and saturation but adding an alpha value, we create a semi-transparent version of the brand color.

Creating Color Variations

Relative colors truly shine when you need to create variations of a base color, such as lighter and darker shades:

:root {
 --base-color: hsl(217 73% 50%);

 /* Create variations from the base */
 --base-light: hsl(from var(--base-color) h s 75%);
 --base-dark: hsl(from var(--base-color) h s 25%);
}

This approach ensures all colors in your palette remain harmonious because they're derived from the same source. When you need to update the base color, all variations automatically update as well.

Practical Component Styling

Relative colors are excellent for styling components that need multiple color states:

.toast {
 --toast-color: #0362fc;

 color: hsl(from var(--toast-color) h s 15%);
 border: 2px solid var(--toast-color);
 background: hsl(from var(--toast-color) h s 90%);
 box-shadow: 0 12px 12px -8px hsl(from var(--toast-color) h s l / 0.325);
}

[data-toast="error"] {
 --toast-color: hsl(0 100% 50%);
}

This pattern creates a cohesive color system for any component while allowing easy customization by simply changing the base color.

Relative Color Examples
1/* Relative color basics */2.btn-primary {3 background: hsl(from var(--brand) h s l / 0.8);4}5 6/* Creating a palette from one color */7:root {8 --brand: oklch(60% 0.18 270);9 10 --brand-light: oklch(from var(--brand) l 80%);11 --brand-dark: oklch(from var(--brand) l 40%);12 --brand-muted: oklch(from var(--brand) s 30%);13}14 15/* Complete component color system */16.notification {17 --base: #2563eb;18 19 background: hsl(from var(--base) h s 95%);20 color: hsl(from var(--base) h s 15%);21 border: 1px solid hsl(from var(--base) h s 80%);22}23 24.notification-success {25 --base: oklch(70% 0.15 145);26}27 28.notification-error {29 --base: oklch(60% 0.18 20);30}

Simplified Theming with light-dark()

Implementing light and dark themes has traditionally required duplicating color values or using complex media query structures. The light-dark() function simplifies this significantly.

Basic light-dark() Usage

The light-dark() function takes two arguments: the light theme color and the dark theme color. The browser automatically selects the appropriate one based on the current color scheme:

:root {
 color-scheme: light dark;

 --text-heading: light-dark(#000, #fff);
 --text-body: light-dark(#212121, #efefef);
 --surface: light-dark(#efefef, #212121);
}

For this to work, you must declare color-scheme with the values light dark, which tells the browser that your page supports both themes and should respect the user's system preference.

Combining with Theme Toggles

When implementing a theme toggle alongside system preference detection, light-dark() keeps your code clean:

:root {
 color-scheme: light dark;

 --text-heading: light-dark(#000, #fff);
 --text-body: light-dark(#212121, #efefef);
 --surface: light-dark(#efefef, #212121);
}

/* Override for specific theme choices */
html[data-theme="light"] {
 color-scheme: light;
}

html[data-theme="dark"] {
 color-scheme: dark;
}

This approach eliminates the need to duplicate color declarations for each theme, reducing both code size and the potential for inconsistencies.

Component-Level Control

A particularly powerful aspect of light-dark() is that it works at any level of specificity. You can override color schemes for individual components:

.hero {
 /* This hero section always uses light theme colors */
 color-scheme: light;

 background: url('hero-image.webp');
}

This allows components to maintain their intended appearance regardless of the overall page theme, which is essential for elements like hero sections with background images that require specific text colors.

Color Interpolation in Gradients

Gradients benefit significantly from modern CSS color features. The ability to specify which color space to use for interpolation can dramatically affect the quality of your gradients.

Understanding Color Interpolation

When you create a gradient from one color to another, the browser calculates the intermediate colors between them. The color space used for this calculation affects the visual result. The traditional sRGB interpolation often produces muddy or washed-out colors in the middle of gradients, especially with highly saturated colors. Interpolation in oklch or lch produces more vibrant gradients without the grayish transition that often appears in sRGB gradients.

/* Default sRGB interpolation */
.gradient {
 background: linear-gradient(to right, blue, red);
}

/* Interpolation in a different color space */
.gradient-better {
 background: linear-gradient(in oklch to right, blue, red);
}

Hue Interpolation Methods

When interpolating through hue-based color spaces, you can control the direction the hue takes around the color wheel. This is particularly important for gradients that need to pass through specific color transitions:

/* Shortest path around the color wheel (default) */
.short {
 background: linear-gradient(in hsl to right, blue, red);
}

/* Longest path for more dramatic color shifts */
.long {
 background: linear-gradient(in hsl longer hue to right, blue, red);
}

/* Increasing or decreasing hue */
.increasing {
 background: linear-gradient(in hsl increasing hue to right, blue, red);
}

The shorter (default), longer, increasing, and decreasing keywords give you precise control over how the gradient transitions through the color wheel.

Gradient Interpolation Examples
1/* Vibrant brand gradient */2.brand-gradient {3 background: linear-gradient(4 135deg in oklch,5 oklch(60% 0.15 250),6 oklch(55% 0.18 320)7 );8}9 10/* Rainbow with smooth transitions */11.rainbow {12 background: linear-gradient(13 in hsl longer hue,14 hsl(0 100% 50%),15 hsl(60 100% 50%),16 hsl(120 100% 50%),17 hsl(180 100% 50%),18 hsl(240 100% 50%),19 hsl(300 100% 50%)20 );21}22 23/* Smooth multi-color gradient */24.smooth-multicolor {25 background: linear-gradient(26 in oklch,27 oklch(50% 0.2 0deg),28 oklch(50% 0.2 120deg),29 oklch(50% 0.2 240deg)30 );31}

Browser Support and Compatibility

Understanding browser support is crucial for making informed decisions about which modern CSS color features to use.

Current Browser Support

Most modern CSS color features have excellent browser support across major browsers:

FeatureChromeSafariFirefox
Modern rgb()/hsl() syntax111+15+113+
lab(), lch(), oklab(), oklch()111+15+113+
hwb()111+15+113+
color() function111+15+113+
Relative colors119+16.4+120+
light-dark()111+18+120+

Progressive Enhancement

Most modern CSS color features are safe to use with progressive enhancement. You can write colors in the new syntax, and browsers that don't support it will ignore the declarations while falling back to the legacy syntax for basic color functions. For features like relative colors and light-dark(), you should test in target browsers and consider providing fallbacks for older browsers if those browsers are in your project's supported list.

Feature Detection

When you need to provide different styles based on feature support, use @supports:

.element {
 /* Fallback for older browsers */
 background-color: rgb(255 0 0);
}

@supports (color: rgb(0 0 0 / 1)) {
 .element {
 /* Modern syntax */
 background-color: rgb(255 0 0 / 0.8);
 }
}

This approach ensures users on older browsers get a working experience while users on modern browsers receive enhanced styles.

Best Practices for Modern CSS Colors

Based on the features and capabilities covered in this guide, here are recommended practices for working with modern CSS colors.

Adopt the Modern Syntax

Start using the space-separated syntax and slash notation for alpha channels. This syntax is well-supported in all modern browsers and consistent across all color functions. It also positions your codebase to take advantage of newer features as you need them.

Choose the Right Color Space

Different color spaces serve different purposes:

  • Use sRGB (via rgb(), hex, or named colors) for simple, universally supported colors
  • Use oklch() or lch() for vibrant gradients and color manipulations
  • Use oklab() for smooth color transitions that maintain perceptual uniformity
  • Use hwb() when you need to adjust colors by adding white or black
  • Use color(display-p3) for colors that need maximum vibrancy on supported displays

Organize Your Color System

Structure your CSS custom properties to take advantage of modern color features:

:root {
 /* Color scheme support */
 color-scheme: light dark;

 /* Base brand colors */
 --brand-primary: oklch(60% 0.15 250);
 --brand-secondary: oklch(65% 0.12 180);

 /* Semantic colors using light-dark() */
 --text-primary: light-dark(#1a1a1a, #f5f5f5);
 --text-secondary: light-dark(#4a4a4a, #b0b0b0);
 --surface: light-dark(#ffffff, #1a1a1a);

 /* Variations created with relative colors */
 --brand-primary-hover: oklch(from var(--brand-primary) l 40%);
 --brand-primary-subtle: oklch(from var(--brand-primary) l 90% / 0.3);
}

This approach creates a maintainable color system where changes to base colors automatically propagate to all derived colors.

Test Across Displays

If your design uses wide gamut colors, test on both wide gamut and standard displays to ensure your colors look good everywhere. Consider providing fallbacks for users on older displays.

Conclusion

Modern CSS colors represent a significant advancement in web design capabilities. From the simplified syntax that makes code more readable to the advanced color spaces that unlock previously impossible visual effects, these features give developers powerful tools for creating visually stunning websites.

The key takeaways from this guide include: embracing the modern space-separated syntax for all color functions; using oklch() or lch() for gradients and color manipulations to maintain vibrancy; leveraging relative colors to create cohesive, maintainable color systems; simplifying light and dark theming with light-dark(); and testing across different browsers and displays to ensure your colors look great everywhere.

As browser support continues to improve and developers become more familiar with these features, we can expect to see increasingly sophisticated and beautiful color usage on the web. Start incorporating these modern features into your web development projects today to take full advantage of what CSS color has to offer.

Ready to Modernize Your CSS?

Implement modern CSS color features to create vibrant, accessible, and maintainable color systems for your web projects. Our experienced web development team can help you leverage these powerful features.

Frequently Asked Questions

Sources

  1. Chrome for Developers - High Definition CSS Color Guide - Comprehensive coverage of CSS Color Level 4 features including wide gamut colors, new color spaces, and HD color support with practical code examples

  2. MDN Web Docs - CSS Color Module Level 4 - Detailed explanation of new color functions (lab, lch, oklab, oklch, hwb, color), syntax changes, and hue interpolation for gradients

  3. Piccalilli - A Pragmatic Guide to Modern CSS Colours Part One - Practical guidance on modern color syntax, relative colors, light-dark() theming, and color space usage for developers

  4. W3C CSS Color Module Level 4 Specification - Official specification for CSS colors