CSS gradients: from flat colour to depth in one line
4 Oct 2026 · Colour · 7 min read
Gradients have a reputation problem. Mention them to a designer of a certain age and they'll flinch, remembering the glossy buttons and rainbow dividers of the early web. But that was never the gradient's fault — it was the taste of the era. Used with restraint, a gradient is one of the cheapest ways to add depth, light and atmosphere to a page: a hero background that glows, a card edge that catches the eye, a button with a hint of dimension. And in CSS, the whole thing is a single background declaration.
This guide builds gradients from first principles: what a gradient actually is, how linear and radial variants work, what colour stops do, and the handful of techniques — hard stops, layered gradients, transparent fades — that separate deliberate design from default-looking mush. By the end you'll be able to write them by hand instead of reaching for a generator every time (though generators are excellent for exploration).
What a gradient actually is
Strip away the syntax and a gradient is simple: a smooth transition between two or more colours across space. The browser computes every pixel's colour by interpolating between the colours you specify, based on each pixel's position. linear-gradient(#2DD4BF, #0EA5E9) says: start teal at one end, end blue at the other, blend everything in between. That's the entire concept. Everything else — angles, stops, shapes — is just controlling where the blending happens and how fast.
Two things surprise beginners. First, gradients are treated as images in CSS, not colours — that's why they go in background-image (or the background shorthand) and why you can layer multiple gradients separated by commas, exactly like multiple background images. Second, the interpolation happens in the sRGB colour space by default, which means the midpoint of a vivid gradient can look muddy or greyish — the infamous "dead zone" between, say, red and green, which passes through a desaturated brown. Newer syntax lets you interpolate in better colour spaces, but the default behaviour explains why some hand-written gradients look unexpectedly dull in the middle.
Linear gradients: direction and angles
A linear gradient blends along a straight line. The default runs top to bottom — linear-gradient(#2DD4BF, #0EA5E9) with no direction given starts teal at the top and ends blue at the bottom. You can aim it with keywords (to right, to bottom left) or with an angle in degrees, where 0deg points up and the angle increases clockwise: 90deg points right, 180deg points down.
The angle plus the element's proportions determine the gradient line: the browser draws the line through the centre at your angle, then extends it to the corners. On a wide, short banner, 90deg gives a clean left-to-right sweep; on a tall card the same angle looks nearly identical because the line adapts. A practical tip: diagonal angles like 135deg (top-left to bottom-right) add energy and are the workhorse of modern hero backgrounds, while subtle near-vertical gradients (180deg with close colours) read as soft lighting rather than as "a gradient".
Colour stops: controlling the blend
With two colours and no positions, the browser spaces them evenly: first colour at 0%, second at 100%. Colour stops let you place each colour exactly where you want it: linear-gradient(90deg, #2DD4BF 0%, #0EA5E9 60%, #7C3AED 100%). Now the teal-to-blue transition finishes at 60% of the way across, and the last 40% blends blue into violet. Stops are how you shape the gradient's rhythm — bunch them together for a rapid shift, spread them for a slow wash.
You can also stack two stops at the same position to create a hard stop: linear-gradient(90deg, #2DD4BF 50%, #0EA5E9 50%) doesn't blend at all — it's teal for the first half and blue for the second, with a razor edge between. Hard stops are the secret behind stripes (#2DD4BF 0 25%, #0EA5E9 25% 50%, #7C3AED 50% 75%, #F59E0B 75% 100% gives four clean bands), progress-bar segments, and the trendy "colour block" backgrounds that look like gradients from a distance but are crisply divided up close. Note the double-position syntax (#2DD4BF 0 25%) — each colour can declare both where it starts and where it ends, which makes multi-band patterns far more readable than repeating positions.
Radial gradients: light from a point
Where linear gradients blend along a line, radial gradients blend outward from a point — the natural shape of light. radial-gradient(circle, #2DD4BF, #0B0E14) glows teal at the centre and dissolves into near-black at the edges. The shape defaults to an ellipse that fills the element, but circle keeps it round, and you can position the centre: radial-gradient(circle at 30% 20%, ...) puts the glow up and to the left, like light falling across the page.
Radial gradients are the atmosphere tool. A large, faint radial glow behind a headline (radial-gradient(circle at 50% 0%, rgba(45,212,191,0.25), transparent 70%)) lifts a dark hero section enormously for one line of CSS. The transparent keyword is doing real work here — fading to transparent rather than to a solid colour lets the page background show through, which keeps the effect soft and avoids banding edges. Vignettes (darkening the corners of a card or image) are the same trick in reverse: radial-gradient(ellipse at center, transparent 55%, rgba(0,0,0,0.45) 100%).
Layering: gradients as building blocks
Because gradients are images, you can stack them: background: radial-gradient(circle at 20% 30%, rgba(45,212,191,0.35), transparent 50%), radial-gradient(circle at 80% 70%, rgba(14,165,233,0.3), transparent 50%), #0B0E14;. The first layer paints on top, the last (here a flat colour) is the base. Two or three soft radial glows over a dark base is the entire recipe behind those rich, expensive-looking dark backgrounds you see on modern landing pages — no images, no blur filters, just layered light.
Layering also enables textures. Repeating gradients (repeating-linear-gradient(45deg, ...)) tile a pattern indefinitely: diagonal pinstripes, checkerboards (two layered repeating gradients at right angles), dot grids (radial gradients with hard stops, sized with background-size). These were party tricks a decade ago; today they're legitimate techniques for subtle paper grain, grid paper, and progress indicators — all resolution-independent and essentially free to render.
| Technique | Syntax sketch | Good for |
|---|---|---|
| Simple linear | linear-gradient(135deg, #2DD4BF, #0EA5E9) | Hero backgrounds, buttons |
| Positioned stops | linear-gradient(90deg, #2DD4BF 0%, #0EA5E9 60%, #7C3AED 100%) | Shaping blend rhythm |
| Hard stops | linear-gradient(90deg, #2DD4BF 50%, #0EA5E9 50%) | Stripes, segments, colour blocks |
| Radial glow | radial-gradient(circle at 50% 0%, rgba(45,212,191,.25), transparent 70%) | Atmosphere on dark sections |
| Layered | radial-gradient(...), radial-gradient(...), #0B0E14 | Rich dark backgrounds |
| Repeating | repeating-linear-gradient(45deg, #2DD4BF 0 10px, transparent 10px 20px) | Pinstripes, textures |
The honest limitations
Gradients have edges — literally and figuratively. First, banding: on smooth, long blends (especially dark ones), you may see faint stripes where the browser runs out of distinct shades between two colours. It's a quantisation artefact, not a bug in your CSS. Dithering would fix it, but CSS has no dithering control; the practical mitigations are keeping blends short, adding a whisper of noise via a layered repeating gradient, or simply choosing colour pairs with less distance between them.
Second, the grey dead zone: interpolating between two vivid colours in sRGB passes through desaturated mud in the middle. Red to green goes through brown; blue to yellow through grey. If your gradient's middle looks dirty, that's why. The modern fix is interpolating in a better colour space — linear-gradient(in oklch, ...) keeps saturation through the blend — supported in all current browsers. It's the single biggest quality upgrade available to gradient authors today.
Third, accessibility: text over a gradient sits on a moving background, which means contrast varies across the line. A headline that's legible at the gradient's dark end may fail at the light end. Always check contrast at the worst point, not the average — or place text on a solid scrim (a semi-transparent overlay) so the gradient becomes decoration rather than a reading surface.
Write them by hand when you know what you want; reach for the generator when you're exploring. Either way, restraint is the whole game — the best gradients on the modern web are the ones you barely notice, quietly doing the work of light.