Color spaces beyond OKLCH: display-p3, a98-rgb, prophoto-rgb, rec2020
While oklch() and lab() describe self-contained color models, the generic color() function opens CSS up to a whole set of named color spaces, including display-p3, a98-rgb, prophoto-rgb and rec2020. Anyone wanting to render product photos or brand colors faithfully on modern displays with a wider color range has to deal with this function, but also needs a clean fallback for older screens and browsers.
Table of Contents
- 1. Why CSS needs more than one color space
- 2. display-p3: the most relevant wide-gamut space for consumer hardware
- 3. a98-rgb and prophoto-rgb: color spaces from professional photography
- 4. rec2020: the color space for HDR video and future displays
- 5. color() compared to oklch() and lab()
- 6. Fallback strategy for browsers and displays without wide-gamut support
- 7. Practical example: product photos and brand colors with display-p3
- 8. Determining wide-gamut values: tooling and conversion
- 9. Browser support status for color() and wide-gamut color spaces
- 10. Summary
- 11. FAQ
1. Why CSS needs more than one color space
Classic sRGB, the web's standard color space since the 1990s, only covers part of the colors modern displays can actually render. A current smartphone or laptop display with a wide-gamut panel can show rich reds and greens that cannot be encoded in sRGB at all, because the color space is simply too small at that point. Images or color values limited to sRGB automatically look slightly more muted on such displays than the hardware could actually deliver.
The generic function color(<color-space> <components>) closes that gap by letting CSS specify color values explicitly in one of several defined color spaces, instead of implicitly always assuming sRGB. Unlike oklch(), which describes a self-contained, perceptually based color model, color() is a generic container whose first argument names the desired color space and whose remaining arguments specify the color values within that space.
.hero-banner {
/* sRGB as the base */
background: rgb(220, 38, 38);
}
.hero-banner.wide-gamut {
/* display-p3 shows a more saturated red on capable displays */
background: color(display-p3 0.86 0.15 0.15);
}
2. display-p3: the most relevant wide-gamut space for consumer hardware
display-p3 is the color space actually in use on current Apple devices, many high-end Android smartphones, and modern monitors. Compared to sRGB, it covers noticeably more of the visible color space, especially in reds and greens, and is the only wide-gamut space that, in practice, is actually rendered in hardware on a meaningful share of devices currently in circulation.
For product photography, especially of objects with rich colors like textiles, cosmetics, or food, the difference between sRGB and display-p3 makes a visible difference in color depth on a capable display. Since display-p3 is also the most broadly supported wide-gamut space, it is the right first choice in the vast majority of cases, before even considering more exotic spaces like prophoto-rgb.
3. a98-rgb and prophoto-rgb: color spaces from professional photography
a98-rgb corresponds to the classic Adobe RGB color space, used for decades in professional photography and print, and offers more color depth than sRGB especially in the cyan-green range. It matters when image material was already produced in Adobe RGB, for example from a professional camera or image-editing pipeline, and needs to be displayed on the web without a lossy conversion.
prophoto-rgb goes a step further and covers a color space larger than any current consumer display can actually render. It is used almost exclusively in professional RAW image-editing workflows, as an intermediate format with maximum color reserve. For web applications, prophoto-rgb is rarely the right choice in practice, since no widespread display can even show its full range, and the values effectively get downmapped to the display's gamut anyway.
4. rec2020: the color space for HDR video and future displays
rec2020, also called BT.2020, is the color space standard for UHD and HDR video and covers an even larger range than display-p3. Current consumer displays can so far only partially exploit this color space, which is why the practical benefit of rec2020 in ordinary web design remains limited today. It becomes relevant primarily for video content already mastered in rec2020, and for projects deliberately preparing for future display generations.
Anyone using rec2020 values in CSS today should be aware that the actually visible color on most current devices is limited to the respective display's gamut anyway, meaning the browser automatically falls back to the best renderable value. That makes rec2020 an investment in the future, whose effect only fully unfolds with broader hardware support.
.video-overlay {
/* rec2020 for future HDR-capable displays,
the browser automatically falls back to the renderable gamut */
background: color(rec2020 0.7 0.05 0.1 / 0.85);
}
5. color() compared to oklch() and lab()
oklch() and lab() describe self-contained, perceptually based color models, in which equal numeric distances are also perceived as equally sized color differences, regardless of the rendering device. That makes them ideal for computing gradients, color palettes, or algorithmically generated color tones, because interpolation in these spaces looks more even than in classic RGB.
The generic color() function, on the other hand, addresses a different question: not how a color is computed or mixed, but in which color space it should actually be encoded and rendered. In practice, both concepts do not exclude each other: a color can be computed in oklch() and then converted for display into a wide-gamut color space like display-p3, if a certain saturation would not even be reachable within sRGB.
6. Fallback strategy for browsers and displays without wide-gamut support
Not every browser supports color() with wide-gamut color spaces, and even where it does, the connected display might still only render sRGB. That's why every use of color(display-p3 ...) needs an sRGB fallback, written as a regular rgb() or hex declaration before the modern declaration, since CSS keeps the last valid declaration of a property when a value is not recognized.
This cascade order works automatically: a browser without wide-gamut support ignores the color() line entirely and stays on the preceding sRGB value, with no @supports check needed at all. What matters is only that the sRGB fallback actually precedes the wide-gamut declaration, since CSS always applies the last valid declaration at equal specificity.
.brand-accent {
/* sRGB fallback first */
color: rgb(217, 70, 239);
/* Wide-gamut value after -- ignored if unsupported */
color: color(display-p3 0.83 0.28 0.93);
}
7. Practical example: product photos and brand colors with display-p3
An online shop with color-intensive products, for example fashion items or cosmetics, benefits most directly from display-p3, because exactly this market segment is where color fidelity of product presentation can be a purchase-deciding factor. Instead of only shipping the product photo itself in a wide-gamut format, it pays off to also define accompanying UI elements like accent colors, badges, or call-to-action buttons in display-p3, so overall brand perception feels consistently richer.
For actual corporate branding, for example a logo's primary color, display-p3 pays off especially when the brand color sits near the edge of the sRGB color space, for example a very saturated pink or orange. If the brand color comfortably sits within the sRGB space already, for example a muted blue or gray, display-p3 brings hardly any visible difference and the extra maintenance effort rarely pays off in that case.
8. Determining wide-gamut values: tooling and conversion
Wide-gamut color values cannot simply be derived from an existing sRGB hex code, because an actual color space conversion is required, not just a numeric transformation. Modern image-editing programs like Photoshop or Affinity Photo display color values directly in the chosen target color space, and several browser-based color picker tools now support direct output of color(display-p3 ...) syntax as well.
Anyone wanting to maintain brand colors consistently across multiple color spaces should store them in a central design token system as a pair of sRGB fallback and display-p3 value, similar to light-dark() value pairs for color schemes. That prevents wide-gamut values from existing only sporadically in isolated spots of the project while the rest of the stylesheet stays limited to pure sRGB.
:root {
--brand-pink-srgb: rgb(236, 72, 153);
--brand-pink-p3: color(display-p3 0.87 0.36 0.58);
}
.badge {
background: var(--brand-pink-srgb);
background: var(--brand-pink-p3);
}
9. Browser support status for color() and wide-gamut color spaces
Safari has fully supported display-p3 through color() for several years already, which is unsurprising given its close ties to Apple's own wide-gamut displays. Chrome and Edge followed with complete color() support, so the function is now production ready across all three major browser engines.
The display side remains the limiting factor regardless: even a browser with full color() support cannot show a visible difference on a classic sRGB monitor, because the hardware simply cannot render the additional hues. The cascade-based fallback mechanism therefore stays sensible for the long term too, independent of pure browser support status.
| Color space | Coverage vs. sRGB | Typical source | Web relevance today |
|---|---|---|---|
display-p3 |
Noticeably more red/green | Apple devices, modern monitors | High, widely available hardware |
a98-rgb |
More cyan/green | Professional photography, print | Medium, niche-specific |
prophoto-rgb |
Larger than any display | RAW image editing | Low, barely renderable |
rec2020 |
Largest of all | HDR video, UHD standards | Low, future investment |
Mironsoft
Modern CSS, layout architecture and rendering performance
CSS that stays maintainable instead of breaking with every change?
We review existing stylesheets for specificity chaos and layout thrashing, then build a CSS architecture with cascade layers, custom properties and modern layout primitives that still makes sense after the tenth feature.
CSS Audit
Systematically uncovering specificity issues, cascade conflicts and unused selectors.
Architecture Refactoring
Introducing cascade layers, custom properties and design tokens cleanly.
Performance Tuning
Fixing layout thrashing, expensive selectors and rendering bottlenecks.
10. Summary
The color() Function in CSS: The Essentials at a Glance
Core idea
color(space values) encodes colors explicitly in a named color space, instead of implicitly assuming sRGB.
display-p3
The most relevant wide-gamut space for today's web projects, broadly supported on consumer hardware.
a98-rgb, prophoto-rgb, rec2020
Special cases from photography and video, of limited practical relevance for most web projects today.
Fallback
An sRGB value before the color() declaration automatically applies in browsers or on displays without wide-gamut support.