P3 and Wide-Gamut CSS on Real Screens
sRGB is not the ceiling on modern hardware. Specify richer colors with fallbacks.
The brand green on a MacBook looked nothing like the same hex on the office sRGB monitor. Neither was wrong. The designer approved #16a34a on a Retina display that renders Display P3. Engineering signed off against an sRGB Dell that clipped the same hex to a duller, smaller gamut. Marketing photographed the product on the MacBook. Support tickets referenced the Dell. Everyone argued from correct evidence.
sRGB was formalized in 1996 as a lowest-common-denominator space for the web. It still is the implicit default when you write hex or rgb(). The hardware landscape changed. iPhones, iPads, and Macs since 2015 ship wide-gamut panels. Many Android flagships use OLED with DCI-P3 coverage. Premium laptops advertise P3 wide color while the tier below advertises one hundred percent sRGB. Your CSS can request colors those screens can show if you write syntax beyond hex.
This article explains Display P3, the color() function, how OKLCH relates to wide-gamut authoring, and a fallback strategy that does not strand sRGB users. References include CSS Color Module Level 4 gamut mapping, MDN on color(), color-gamut, and Björn Ottosson’s OKLCH as an authoring space that can specify out-of-sRGB colors.
sRGB versus Display P3 in plain terms
A gamut is the set of colors a device can produce. sRGB occupies roughly thirty-five percent of visible color in the CIE 1931 chromaticity diagram. Display P3, based on DCI-P3 with a D65 white point, covers about forty-five percent. The extra territory lands in greens, reds, and oranges, the saturated hues sRGB clips hardest.
Practical consequence: the greenest green an sRGB monitor can show is not the greenest green a P3 MacBook can show when you ask in P3 coordinates. The same hex on both machines resolves to the sRGB green mapped into each device’s capability. You are not imagining the difference between design mockups on a P3 display and production on sRGB. You are seeing gamut mismatch.
Modern CSS offers overlapping paths. OKLCH with high chroma lets browsers convert and gamut-map per CSS Color 4 rules. On sRGB displays, high-chroma OKLCH compresses toward the nearest reproducible color. On P3 displays, more requested chroma survives. You author one declaration and the browser adapts. The color() function takes an explicit color space name and channel values. For web UI work, display-p3 matters day to day. The color-gamut media feature lets you branch on display capability for progressive enhancement.
Three authoring paths and cascade order
OKLCH is the lowest-friction path if you have already migrated palettes to OKLCH. Wide-gamut enhancement may be as simple as increasing chroma on hero tokens within brand contrast limits. Explicit P3 coordinates matter when design exports Display P3 triplets from a calibrated pipeline and legal brand guidelines reference those numbers.
.brand-green {
background-color: #16a34a;
background-color: oklch(0.65 0.20 145);
}
@supports (color: color(display-p3 0 0 0)) {
@media (color-gamut: p3) {
.brand-green {
background-color: color(display-p3 0.12 0.78 0.34);
}
}
}
Fallback order matters. Later declarations win only when the browser understands them. Ship hex or rgb() first as the universal baseline. Layer OKLCH second for modern browsers on any gamut. Layer P3-specific values third only when both syntax and hardware support it. Putting sRGB after OKLCH breaks the cascade on browsers that understand both.
Wide-gamut CSS is progressive enhancement, not a breaking change. Layer one is sRGB hex every browser understands. Layer two is OKLCH override with automatic gamut mapping. Layer three is P3-specific boost on wide-gamut hardware only, appropriate for hero backgrounds and marketing gradients, not body text or one-pixel borders.
When marketing green outran production contrast
A consumer hardware company refreshed brand guidelines with a vivid green specified in Display P3 for packaging and keynote slides. Web engineering transliterated the P3 triplet into OKLCH with high chroma and shipped a single declaration without sRGB fallback or contrast re-check on clipped values. On designers’ MacBooks, the hero looked aligned with physical product photography. On the majority sRGB audience, the green clipped to a duller shade that still passed contrast against white text. On a middle slice of P3 laptops with aggressive chroma, the green passed contrast in authored values but failed against white text after gamut mapping dulled the fill unexpectedly on certain GPU paths.
The failure mode was treating out-of-gamut OKLCH as exact across devices. WCAG contrast on the authored OKLCH value differed from the clipped value users saw. Support screenshots mixed Dell monitors and iPhones in the same thread. Engineering initially blamed monitor calibration until someone photographed screens side by side and attached images to the ticket.
Remediation shipped three layers: sRGB hex baseline, OKLCH at moderate chroma for automatic mapping, and a color-gamut: p3 block with a modest chroma bump, not a jump from zero point one eight to zero point four zero. Contrast checks ran on computed styles in DevTools on both device classes. Dark mode P3 bumps reduced chroma further because saturated greens on near-black halated. Gradients received in oklch interpolation so midpoints did not mud through gray when P3 endpoints were vivid.
Legal brand reviewed the staged screenshots and accepted a written policy: web hero greens may exceed sRGB on P3 hardware up to a chroma ceiling that still passed four point five to one against white button labels on computed values, not authored values alone. Packaging continued to use explicit P3 coordinates from the print pipeline. Web used OKLCH with conditional enhancement. The split ended months of arguing whether the Dell monitor or the MacBook was the true brand, because each device class had a named layer in the guideline document instead of a single contested hex.
The brand team accepted that web green would not match printed P3 green pixel-perfect on every screen. The contract became richest safe green per device class, not one number everywhere. That honesty ended the hex argument because each layer had a named job.
Gradients, gamut mapping, and testing reality
Solid fills show subtle wide-gamut benefit. Gradients show dramatic benefit because interpolation paths spend more time in high-chroma territory. OKLCH interpolation avoids the gray dead zone. P3 gamut preserves chroma sRGB would clip at endpoints and midpoints. Combine in oklch gradient syntax with P3 chroma bumps only where brand demands visible richness.
When OKLCH or color(display-p3 …) specifies colors outside the display gamut, CSS Color 4 requires gamut mapping. Engines typically clip or compress chroma while preserving lightness and hue intent. Do not treat extreme OKLCH chroma as identical across devices. Prefer modest chroma boosts. Increment and compare on sRGB and P3 hardware. Contrast checks must use computed colors, not authored values alone.
Simulators lie. Chrome DevTools can emulate color-gamut: p3, but an sRGB laptop screen cannot show P3 photons. It emulates the cascade, not the output. Minimum test matrix includes an sRGB monitor, a P3 MacBook or iPhone, and an external sRGB panel attached to a P3 machine because macOS may report wide gamut for the system while the external display is sRGB.
OKLCH high chroma offers one syntax with automatic gamut map and perceptual edits. color(display-p3 …) offers exact P3 coordinates from design tools with manual fallbacks. Most teams should author tokens in OKLCH, use the hex-then-OKLCH pattern, add color-gamut: p3 chroma bumps only for hero tokens, and use explicit color(display-p3 …) only when design exports calibrated triplets.
Accessibility and common mistakes
Richer color is not better color for everyone. Highly saturated P3 greens and reds may fail WCAG contrast when chroma bumps push fills closer in luminance to adjacent text. They can increase glare for light-sensitive users in dark mode. They do not improve color-blind discrimination by themselves. Re-run contrast on P3-enhanced tokens against light and dark surfaces.
P3-only palettes with no sRGB fallback strand users when syntax is unsupported or values clip to mud. Assuming @media (color-gamut: p3) means the active monitor is P3 ignores multi-display setups. Converting hex to P3 by naive RGB scaling produces wrong coordinates; convert through proper algorithms or DevTools. Wide-gamut photography with flat sRGB UI chrome looks disjointed. P3 enhancement on dark backgrounds can halate if chroma is not reduced alongside lightness bumps.
color-gamut: rec2020 and color(rec2020 …) exist for HDR and ultra-wide-gamut displays. Consumer web usage remains niche in 2026. The same progressive enhancement pattern applies, but most teams should not author rec2020 until analytics justify it.
:root {
--brand-hero: #16a34a;
--brand-hero: oklch(0.65 0.18 145);
}
@supports (color: oklch(0% 0 0)) {
@media (color-gamut: p3) {
:root {
--brand-hero: oklch(0.65 0.26 145);
}
}
}
.hero {
background: var(--brand-hero);
}
Photography and CSS chrome should be aligned in the same release when marketing cares about color fidelity. A P3 hero photograph above sRGB flat UI buttons makes the buttons look washed out even when the buttons are technically correct. Bump semantic token chroma modestly on P3 hardware so interface elements do not feel like a different brand layer than the photography they frame. You are not matching print ink. You are preventing disjointed perceived saturation across media types on the same viewport.
Authoring workflow for most teams remains OKLCH-first with optional P3 enhancement, not a parallel P3 palette maintained by hand. Evil Martians and other practitioners argue for single source coordinates with chroma multipliers under color-gamut: p3 rather than maintaining separate hex and P3 triplets per token. Explicit color(display-p3 …) enters when legal brand guidelines or calibrated print pipelines export numbers that must be reproduced exactly on Apple hardware, not approximated through OKLCH mapping.
Educate stakeholders with side-by-side photographs early. Designers and PMs argue from mockups on laptops they use daily. Engineering argues from CI screenshots on sRGB agents. A single shared photo of two screens running the same URL ends circular debates faster than another spreadsheet of hex values. Document the three-layer strategy in brand guidelines so external agencies do not ship P3-only CSS because their office monitors happen to be wide gamut.
Future HDR and rec2020 syntax exists in specifications and media queries. Consumer web usage in 2026 remains niche outside specialized video and gaming experiences. The progressive enhancement muscle you build for P3, baseline first, enhanced second, explicit wide gamut third, transfers directly when rec2020 hardware justifies another branch. Do not skip the discipline of layer one because layer three feels futuristic.
Brand compliance teams sometimes ask for a single hex in guidelines because legal documents prefer one number. Web engineering can document that the canonical brand hex is the sRGB layer while P3 enhancement is an optional richness layer that does not change semantic meaning. Legal gets one anchor. Engineering gets three layers. Design reviews staged screenshots on both device classes to confirm the story matches packaging within agreed tolerance. That framing prevents compliance from blocking wide-gamut CSS outright while keeping accessibility evidence tied to computed colors users actually receive.
Embedded WebViews in enterprise mobile apps may report color-gamut: p3 while rendering through software paths that dull saturation. Test embedded browsers separately from Safari and Chrome on the same phone hardware. Analytics that segment WebView user agents help decide whether Layer three deserves maintenance in contexts where users cannot see the enhancement anyway.
Video and CSS chrome on the same marketing page sometimes mix wide-gamut video with sRGB UI because video pipelines were updated before token files. Align at least roughly: if video greens are vivid P3, flat sRGB buttons beside the player look broken even when each layer is technically correct in isolation. Coordinate launches so token chroma bumps ship in the same release as new hero video assets when brand cares about cohesive saturation.
Consumer education is rarely required, but internal education is. Designers should know that Figma hex preview is sRGB-biased while approved web CSS may intentionally request more chroma on capable hardware. Engineers should know that Layer three is optional enhancement, not the baseline contract with accessibility. PMs should know that arguing over one hex between laptops is often a gamut conversation, not a mistake by either side.
Print and web color workflows diverge by medium constraints, not by designer preference alone. CMYK ink cannot reproduce every P3 screen green. Web tokens should not chase print numbers that clip on most laptops. Instead, document acceptable delta between packaging photography, web hero CSS, and sRGB baseline hex so stakeholders judge each medium against its own layer rather than against an impossible single swatch.
Color-managed browsers and operating system settings affect perceived output beyond CSS syntax. Users with increased contrast or desaturated accessibility settings may see your P3 enhancement reduced regardless of color-gamut queries. Wide-gamut CSS is enhancement for typical viewing paths, not a guarantee of identical output across every accessibility configuration. Re-run spot checks with increased contrast enabled on macOS and Windows when brand reviews wide-gamut launches.
sRGB users get a correct green. P3 users get a greener green. One token name, one component, zero forked markup. The hex fallback honors the implicit default of the web. The OKLCH middle layer honors CSS Color 4 gamut mapping. The color-gamut branch honors the hardware on desks and in pockets. Stop arguing about which screen is right. Ship all three layers and let each device take the best color it can show.