Home · Blog · Colour

CSS gradients: a practical guide to linear, radial and conic

Gradients are easy to write and surprisingly easy to get wrong: muddy midpoints, visible banding, text that fails contrast at one end. This guide covers the three gradient types, colour-stop craft, interpolation in OKLCH, and the performance and accessibility rules that separate polish from decoration.

A CSS gradient is not an image; it is a computed background, generated by the browser from colour stops you declare. That makes gradients resolution-independent, trivially small in file terms, and animatable in ways images are not. It also means their quality is entirely your responsibility: the browser will faithfully render a muddy, banded, illegible gradient exactly as specified.

The craft divides into knowing the three shapes (linear, radial, conic), placing stops with intent, choosing the colour space the blend happens in, and respecting two hard constraints — banding on smooth ramps, and text contrast over a background that changes value by definition.

Key takeaways

  • Specify direction in degrees or to-side terms, and place stops with explicit positions — defaults hide surprises.
  • Interpolate in oklch (or hsl at a pinch) for blends that pass through other hues cleanly; default sRGB mixing greys the midpoint of vivid pairs.
  • Banding is worst in dark, slow gradients; a whisper of noise or a shorter ramp hides it better than more stops.
  • Text over a gradient must pass contrast at the worst point of the area it occupies — or get a solid backdrop.

The three shapes and their honest uses

TypeSyntax sketchNatural jobs
linear-gradientlinear-gradient(135deg, A, B)Surface washes, hero backgrounds, progress and skeleton shimmer, image scrims (transparent → dark)
radial-gradientradial-gradient(circle at 30% 20%, A, B)Spotlights and glows, vignettes, focus states, subtle depth behind a product shot
conic-gradientconic-gradient(from 210deg, A, B, A)Pie and donut charts, colour wheels, angular borders and loading rings

Each type also has a repeating variant (repeating-linear-gradient and friends) that tiles the stop pattern — the honest way to make stripes, hazard bands and graph paper without images. If you find yourself writing the same two stops eight times, the repeating form was the tool.

Colour stops: where gradients are actually designed

A stop is a colour plus a position, and both halves matter. linear-gradient(90deg, #ff7a29, #ff5da2) blends evenly across the whole width; adding an unpositioned middle stop pulls the blend toward it; pinning positions — #ff7a29 0%, #ff5da2 55%, #7a2bff 100% — decides exactly where each colour holds. Two stops at the same position make a hard edge, which turns gradients into a crisp tool for split backgrounds and two-tone panels, not just soft blends.

/* Sunset wash with a held middle */
background: linear-gradient(
  160deg,
  oklch(0.82 0.12 85) 0%,
  oklch(0.70 0.18 47) 45%,
  oklch(0.55 0.20 15) 100%);

/* Hard two-tone split */
background: linear-gradient(90deg,
  var(--surface) 0 62%, var(--accent-soft) 62% 100%);

Interpolation space is the modern subtlety. CSS now lets you declare it: linear-gradient(in oklch, red, blue). The default (sRGB) path between saturated opposites sags through grey-brown; the OKLCH path swings around the hue wheel and stays vivid — sometimes too vivid, producing hues you never chose in the middle. Neither is "correct": muted and corporate wants the sRGB sag, vivid and illustrative wants the wheel. Choose consciously, per gradient.

Banding, and the three ways out

On slow ramps — especially dark ones, and doubly so on 8-bit displays — a gradient steps visibly instead of flowing, because adjacent pixel columns round to the same colour values. Adding more stops does not fix it; the steps are in the output values, not your declaration. What works: shortening the ramp (blend over less distance or between closer colours), increasing the angle so steps spread diagonally across more pixels, or — the classic — overlaying a tiny amount of noise (an SVG turbulence background at 2–4% opacity) to dither the bands away. If a hero background bands on your test monitor, it bands worse on a cheap one; fix it in the CSS, not in your head.

Performance and accessibility rules

  • Gradients are nearly free to download, not always free to paint. A full-screen gradient behind scrolling content forces repaints; animating gradient position is a known scroll-jank source. Keep big animated gradients subtle, slow and preferably on compositor-friendly properties.
  • Never let body text ride a gradient unprotected. Measure contrast at the lightest (for dark text) point behind the actual glyphs, or place text on a solid or scrimmed area. Hero headlines over scrims pass; the same headline over the raw photo-gradient usually does not.
  • Print and forced-colour modes drop backgrounds. In Windows forced-colors mode your gradient vanishes entirely. Anything the gradient alone communicates — a chart segment, a state — needs a non-colour carrier too: a label, an icon, a pattern.
  • Respect reduced motion. Animated gradient washes count as motion; gate them behind prefers-reduced-motion like any other animation.

Mistakes and pitfalls

The five-stop rainbow default

Tool defaults that drop four intermediate hues into a two-colour blend create the busy, dated look people associate with gradients generally. Most professional gradients are two colours, close in hue, differing mainly in lightness.

Judging a gradient on the design canvas only

Gradients stretch. A blend tuned in a 400-pixel artboard behaves differently across a 1600-pixel hero — midpoints land in different places and banding appears. Always evaluate at production size, on the page.

Using a gradient to rescue a weak colour pairing

If two colours clash flat, blending them manufactures a third problem in the middle. Fix the pairing first (lightness and chroma, per the colour-theory article), then decide whether a blend adds anything.

Common questions

Linear or radial for a hero background?

Linear for direction and structure — it reads as design. Radial for light: a soft glow behind a headline or product. Combining a faint radial highlight over a linear wash is the combination most polished heroes actually use.

Can I animate a gradient smoothly?

You can animate stop positions via @property-registered custom properties in modern browsers, or crossfade two stacked layers with opacity — the latter is universally smooth and simple. Animating background-size/position works but repaints constantly; keep it off long pages.

Why does my gradient look different in Safari?

Historically, older syntax and colour-space handling differed; today the common culprit is an unclamped OKLCH colour rendering inside gamut in one browser and clamped in another. Test vivid OKLCH blends in every target browser, or keep stops inside sRGB gamut for consistency.

To wrap up

A gradient is a small program that runs on every pixel of a surface. Write it like one: deliberate shape, positioned stops, a chosen interpolation space, contrast and banding checked at production size. Two close colours, honestly blended, will out-design a rainbow every time.

When in doubt, subtract. The gradients that age best in production are close to flat: a few points of lightness between their stops, one quiet hue decision, and nothing the layout would grieve if it were removed. Treat every gradient as obliged to justify itself — it should be doing a job (depth, focus, brand atmosphere, a scrim's legibility) that a flat colour demonstrably cannot do, and it should survive forced-colour modes and print with the content's meaning intact. If removing a gradient changes nothing, removing it is the design improvement, and the page gets lighter as a bonus.

Try it yourself

The gradient generator gives you two-colour and multi-stop blends with direction control and ready-to-copy CSS, including modern colour-space interpolation — experiment there before the values go near production.

Open the Gradient Generator