A CSS gradient interpolates between colour stops. The syntax is simple; what separates a gradient that looks expensive from one that looks like a 2010 web template is almost entirely about which colours you pick and which space they are blended in.
The dead zone problem
Blend two saturated colours of opposing hue in sRGB — blue to yellow, say — and the midpoint turns a muddy grey. This happens because straight-line interpolation in RGB passes through the centre of the colour cube, where saturation collapses.
Two fixes:
Interpolate in a better space. Modern CSS lets you specify one:
linear-gradient(in oklch, #3d8bf5, #f5b73d). OKLCH keeps chroma up across the transition, so
the midpoint stays a genuine colour instead of turning to mud. Support is now broad across
current browsers, with the sRGB version as the natural fallback.
Add a midpoint stop. Insert a colour that sits where you want the middle to be — often a hue between the two endpoints at full saturation. This works everywhere and gives you direct control.
Keeping it subtle
The gradients that read as designed rather than decorative tend to follow a few patterns:
- Small hue travel. 30–60° around the wheel. Blue to purple, orange to pink. Large hue jumps look dated unless very deliberately handled.
- Lightness does most of the work. Vary lightness substantially and hue only a little. This is what most “premium” gradients actually are.
- Angle with intent.
135deg(top-left to bottom-right) reads as natural light. Vertical gradients read as sky. Horizontal ones tend to look flat. - Restraint on text. A gradient behind body copy makes contrast vary across the block, and the worst point is the one that has to pass. Check both ends with the contrast checker.
Banding
Gradients over a large area — full-page backgrounds especially — often show visible stripes,
because 8-bit colour cannot supply enough intermediate steps across that distance. Fixes, in
order of preference: reduce the distance the gradient covers; reduce the lightness range; add
a very subtle noise texture overlay, which breaks up the banding perceptually; or use a
radial-gradient where the transition is less linear.
Types worth knowing
Linear — travels along an axis. The default choice.
Radial — spreads from a point. Good for spotlights, glows and soft backdrops. Control the
shape with circle or ellipse and reach with closest-side / farthest-corner.
Conic — sweeps around a centre. The basis of pie charts, colour wheels and loading spinners built without images.
Repeating variants of each produce stripes and rings, which are useful for patterns and almost never for backgrounds.
Where people go wrong
Too many stops. Three is usually the maximum before a gradient stops reading as one surface. Long rainbow gradients look busy at any size.
Ignoring the fallback. Always give a solid background-color before the gradient, so
anything that cannot render it still gets a sensible surface — and so text over it stays
readable.
Gradients on small elements. On a button 40px tall, a gradient is invisible at best and muddy at worst. Save them for large surfaces.
Forgetting dark mode. A gradient tuned for a light background usually needs different stops when inverted, not just a filter.
Using transparent as a stop. In sRGB, transparent is transparent black, so fading a
colour to it darkens the transition. Fade to the same colour at zero alpha instead —
rgb(61 139 245 / 0) — which stays clean.
Once you have a gradient you like, shades and tints will give you the matching solid colours for text and borders, and export will hand you the CSS.