CSS Gradients for Dark Mode UI
Designing gradients for dark mode requires a different approach to color than light mode, because high-chroma stops that read as vibrant accents on a white surface appear neon or washed out against a dark background. Effective dark-mode gradients reduce chroma below 0.15 in oklch terms, keep lightness stops between 0.12 and 0.25, and use very small lightness deltas to create depth without visual noise.1
The prefers-color-scheme media query swaps gradient stops by reassigning CSS custom properties, keeping the gradient declaration itself unchanged.2 Consequently, a single gradient function reference adapts to both themes through the custom property values. Building on this, subtle surface gradients with lightness deltas of 0.04 to 0.08 add depth to sidebars, cards, and hero sections without the pronounced banding that appears when dark-mode gradients use the same contrast ratios as light-mode designs.
High-chroma gradients on dark surfaces
A gradient that uses oklch(0.7 0.25 250) to oklch(0.65 0.22 300) works well on a white or light grey background. On a dark surface (background around oklch(0.12 0 0)), those same stops appear luminous and neon rather than atmospheric.3 The high chroma relative to the dark surrounding context creates an extreme contrast that draws attention away from UI content. This effect is especially pronounced on OLED displays where the dark background emits no light at all, making the gradient stops appear to float as self-illuminated color fields against a void.
Dialing chroma down to the 0.08 to 0.15 band is what keeps a dark-mode gradient from reading as neon while the color stays visible. Using oklch(0.4 0.1 250) to oklch(0.35 0.08 300) on a dark background produces a muted blue-purple gradient that reads as a surface tone rather than a focal accent. Yet some UI elements like call-to-action buttons intentionally use high-chroma gradients even on dark surfaces: a primary button at oklch(0.65 0.22 250) stands out from the dark surrounding content as intended. Building on this, the chroma reduction rule applies to large surface areas (hero backgrounds, sidebar panels, cards) rather than small interactive elements where high contrast is the goal.
Using prefers-color-scheme to swap gradient stops
CSS custom properties let you define gradient stop colors once and swap them at the variable level.4 Declare custom properties for the start and end stops in :root with light-mode values, then redefine them inside @media (prefers-color-scheme: dark). The gradient background-image declaration stays unchanged. The same pattern works for theme toggling via a data-theme attribute by scoping the property overrides to [data-theme="dark"] instead of the media query.
Custom property swap pattern
Define the properties in :root with light-mode oklch values, then reassign them inside the prefers-color-scheme: dark query with lower lightness values. The gradient function references both properties with var(), so the gradient adapts to the user's system preference with no JavaScript and no duplicated gradient declarations. The same pattern works for theme toggling via a data-theme attribute by scoping the property overrides to [data-theme="dark"] instead of the media query.
When the user switches between light and dark mode at the operating system level, the browser re-evaluates the media query and the gradient transitions immediately to the new color values. For projects that support both system-level preference and a manual theme toggle, defining the custom properties under both the media query and a data-theme selector covers all three scenarios: system light, system dark, and manual override. Adding a CSS transition on the custom property values combined with @property registration produces a smooth animated gradient shift when the user toggles between themes.
Subtle surface gradients with small lightness deltas
Dark UI surfaces often benefit from a gradient with a very small lightness difference rather than a color difference. A gradient from oklch(0.14 0 0) to oklch(0.18 0 0) is nearly achromatic but creates visible depth on a dark card or panel. The lightness delta of 0.04 is large enough to read as a gradient but small enough not to appear as a color shift. Testing the chosen tint on both OLED and LCD panels reveals whether the subtle hue shift reads as intended or disappears entirely, since OLED true-black backgrounds amplify the perceived tint compared to LCD panels with backlight bleed.
Adding hue to subtle gradients
A slight hue addition enhances the depth gradient without introducing visible color. Moving from completely achromatic (C = 0) to a chroma of 0.03 to 0.05 adds a barely perceptible tint. For example, oklch(0.14 0.03 250) to oklch(0.20 0 0) transitions from a very slightly blue-tinted dark to a neutral dark. The hue is visible up close but reads as neutral at a glance. Furthermore, subtle blue tints at low chroma tend to read as modern and technical; warm tints at the same low chroma can make dark surfaces feel more premium. The choice depends on the product's design language rather than technical constraints. Testing the chosen tint on both OLED and LCD panels reveals whether the subtle hue shift reads as intended or disappears entirely, since OLED true-black backgrounds amplify the perceived tint compared to LCD panels with backlight bleed.
Cross-browser testing strategy for dark-mode gradients
Safari on macOS color-manages CSS gradients differently than Chrome on the same display. On a P3 display such as the MacBook Pro M4, Safari renders oklch() colors with accurate P3 gamut mapping, while Chrome maps oklch() values through sRGB before passing them to the display.5 For dark-mode gradients using oklch() with low chroma, this difference is minor. For gradients with chroma above 0.15, checking the gradient in both browsers on a P3 display reveals any color rendering discrepancy before deployment.
Testing dark-mode gradients also requires verifying the prefers-color-scheme swap in isolation. Open DevTools in Chrome and switch the Rendering panel's Emulate CSS media feature to prefers-color-scheme: dark without enabling a system-level dark mode. This lets you inspect the dark-mode custom property values and the resulting gradient in the same browser session used for light-mode development. Safari's Web Inspector provides the same emulation under the Responsive Design Mode settings.
OLED display verification
Dark-mode gradients intended for OLED displays (iPhone 17 Pro, Samsung Galaxy S25, many premium Android phones) should be tested directly on those devices. Simulator-level testing in Xcode or Android Studio renders gradients using the workstation display profile rather than the device's OLED panel. A subtle lightness delta that reads clearly on a P3 IPS display may appear as banding on an OLED due to the different gamma curve at near-black luminance values.
When to use this
Use dark-mode specific gradient stops for large surface areas: hero backgrounds, sidebars, card panels, and page backgrounds. Retain higher chroma for interactive elements like primary buttons and active state indicators where visibility is the priority.
Examples
Dark hero background with subtle blue-to-neutral gradient
Use oklch(0.14 0.04 250) to oklch(0.18 0 0) for a dark blue-to-neutral sweep. The lightness delta reads as depth, and the chroma drop from 0.04 to 0 adds a color character without appearing neon. Apply via CSS custom properties and swap in the prefers-color-scheme query.
Card surface gradient that adapts to light and dark themes
Define --card-from and --card-to in :root with light values and override them in the prefers-color-scheme: dark query. The card background: linear-gradient(135deg, var(--card-from), var(--card-to)) adapts automatically. No JavaScript or class toggling required.
Sidebar panel with a vertical depth gradient for dark mode
Apply a top-to-bottom gradient from oklch(0.16 0.02 250) at the top to oklch(0.12 0 0) at the bottom. The darker bottom anchors the sidebar visually to the page footer. Keep the chroma difference minimal (0.02 maximum) to avoid color banding on high-DPI displays.
- 1.
MDN Web Docs, "oklch() CSS function," developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/color_value/oklch
- 2.
MDN Web Docs, "prefers-color-scheme CSS media feature," developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/prefers-color-scheme
- 3.
Thomas Steiner, "prefers-color-scheme: Hello darkness, my old friend," web.dev, accessed June 2026. https://web.dev/articles/prefers-color-scheme
- 4.
CSS-Tricks, "oklch()," css-tricks.com, accessed June 2026. https://css-tricks.com/almanac/functions/o/oklch/
- 5.
Nikita Vasilyev, "Wide Gamut Color in CSS with Display-P3," webkit.org, March 2020. https://webkit.org/blog/10042/wide-gamut-color-in-css-with-display-p3/