Home / Snippets / Color & Theming /

Dark mode gradients

Gradient backgrounds tuned for dark interfaces — using oklch() with low lightness and controlled chroma so they glow rather than wash out.

Linear
135deg, purple → violet
Radial
Ellipse, teal glow
Conic
Sweep from 200deg
Multi-stop
4 stops, 160deg
Widely Supported
colorno-js

Quick implementation

/* Linear gradient — directional sweep */
.grad-linear {
  background: linear-gradient(
    135deg in oklch,
    oklch(0.18 0.06 265),
    oklch(0.38 0.18 300)
  );
}

/* Radial gradient — glowing focal point */
.grad-radial {
  background: radial-gradient(
    ellipse at 30% 40% in oklch,
    oklch(0.42 0.20 190),
    oklch(0.16 0.04 265)
  );
}

/* Conic gradient — full sweep */
.grad-conic {
  background: conic-gradient(
    from 200deg at 60% 50% in oklch,
    oklch(0.20 0.08 265),
    oklch(0.40 0.18 310),
    oklch(0.28 0.14 230),
    oklch(0.20 0.08 265)
  );
}

/* Multi-stop linear — rich tonal range */
.grad-multi {
  background: linear-gradient(
    160deg in oklch,
    oklch(0.15 0.05 260),
    oklch(0.30 0.16 285) 35%,
    oklch(0.45 0.20 320) 65%,
    oklch(0.22 0.08 240)
  );
}

Prompt this to your LLM

Includes role, constraints, four gradient variants, and edge cases to handle.

You are a senior frontend engineer designing gradient backgrounds
for a dark-mode-only UI.

Goal: Create four CSS gradient patterns (linear, radial, conic, multi-stop)
that look rich and intentional on dark backgrounds — they should glow, not
wash out or appear washed-out gray.

Technical constraints:
- Use oklch() color syntax exclusively — no hex, hsl, or rgb values.
- Add "in oklch" to each gradient function so the browser interpolates
  through oklch space rather than sRGB, avoiding the gray muddy midpoint.
- Keep lightness values in the 0.15–0.45 range. Higher lightness
  (above 0.6) creates harsh, blown-out patches on dark backgrounds.
- Use chroma values of 0.08–0.22. Below 0.05 looks gray; above 0.25
  risks out-of-gamut clipping on sRGB displays.
- Hue angles: pick hues that are adjacent (within ~60 degrees) for
  harmonious blends, or complementary (~180 degrees apart) for contrast.
- Do not use white or near-white stops in dark-mode gradients — they
  create glare and destroy the dark ambiance.

Gradient variants to produce:
1. Linear: 135deg sweep, two stops, purple-to-violet family.
2. Radial: ellipse with off-center focal point, teal glow fading to dark.
3. Conic: full circular sweep starting at ~200deg, 3-4 stops.
4. Multi-stop linear: 4+ stops with explicit percentages for fine control.

Edge cases to handle:
- Banding: if stops are too far apart in lightness, add intermediate
  stops to smooth the transition.
- Interpolation method: always include "in oklch" in the gradient
  function — without it, browsers interpolate in sRGB which produces
  a gray/brown desaturated midpoint.
- Fallback: provide a solid oklch() background-color before the gradient
  for browsers that do not support the gradient syntax used.
- Over-saturation at midpoints: oklch interpolation can briefly spike
  chroma at the midpoint between two hues. Use an intermediate stop
  with lower chroma to suppress this if needed.

Return CSS only — class selectors and the background declarations.

Why dark mode gradients are different

A gradient that looks gorgeous on a white background will often look washed out, gray, or glaring on a dark background. This is not a matter of taste — it is a consequence of how luminance perception works. On a light background, the eye adapts to a high ambient luminance; colors at 70–90% lightness feel vibrant. On a dark background, the eye adapts downward, and those same light colors become eye-strain-inducing blobs of light. The comfortable range for a dark-mode gradient stop is roughly 0.15–0.45 lightness in oklch.

Chroma also needs adjustment. At very low lightness (below 0.20), the eye becomes more sensitive to saturation, so even modest chroma values of 0.08–0.12 can read as vivid. At the higher end of the dark-mode lightness range (around 0.40–0.45), you can push chroma toward 0.18–0.22 for a glowing accent without clipping out of sRGB gamut.

oklch interpolation in gradients

By default, CSS gradients interpolate in sRGB. When you blend two saturated colors — say a blue and a magenta — the midpoint passes through a low-chroma zone that produces a muddy gray-brown band. This is the infamous "gray gradient problem."

Adding in oklch to the gradient function instructs the browser to interpolate in oklch space instead. Because oklch separates lightness, chroma, and hue, the browser can keep chroma elevated while the hue rotates smoothly between the two endpoints. The result is a vivid, evenly-luminous transition with no gray muddy patch.

The syntax is simply appended after the angle or shape keyword:

  • linear-gradient(135deg in oklch, ...)
  • radial-gradient(ellipse at 30% 40% in oklch, ...)
  • conic-gradient(from 200deg in oklch, ...)

One edge case: when interpolating hues that are far apart (more than ~120 degrees), oklch takes the short arc by default. If you want the gradient to travel through the warm or cool spectrum on a specific path, you can append shorter hue or longer hue to the interpolation keyword: in oklch shorter hue.

Avoiding banding

Banding — visible stripes in what should be a smooth gradient — has two main causes in dark-mode gradients. The first is a large lightness jump between stops with too few intermediate values. At low lightness levels, the eye is more sensitive to contrast, so a jump from 0.15 to 0.45 in two stops can produce a visible seam. Adding an explicit middle stop at the interpolated values gives the browser more control over the curve.

The second cause is a chroma spike at the midpoint. When two hues are far apart in the hue wheel, oklch can briefly spike chroma as it rotates through intermediate hues. Suppress this by inserting a middle stop with the same intermediate hue but a lower chroma value — for example, oklch(0.30 0.10 282) between a blue stop and a purple stop will dampen the spike without breaking the visual flow.

A practical rule of thumb: keep consecutive stops within 0.20 lightness units and within 80 hue degrees of each other. If you need a bigger jump, add intermediate stops.