CSS gradients are rendered by the browser rather than downloaded. No HTTP request, no file size, infinitely scalable, trivially themeable. For backgrounds, buttons and overlays they are strictly better than an image.
They are also easy to make look cheap. Two specific problems account for most of it.
The syntax, briefly
Linear gradients run along a straight line. Direction comes first — an angle or a keyword.
background: linear-gradient(135deg, #7c6cff, #38bdf8);
background: linear-gradient(to bottom right, #7c6cff, #38bdf8);0deg points up, 90deg points right. This trips people up because it differs from the maths convention.
Radial gradients spread outward from a point.
background: radial-gradient(circle at 30% 40%, #7c6cff, transparent);Colour stops control where each colour sits:
linear-gradient(90deg, red 0%, yellow 30%, blue 100%)That compresses the red-to-yellow transition into the first third.
Problem one: the grey midpoint
Browsers interpolate gradients in sRGB, blending the red, green and blue channels independently. For colours on opposite sides of the colour wheel, that path passes through a desaturated middle.
Blue to yellow is the classic offender. Blue is roughly (0, 0, 255), yellow is (255, 255, 0). The midpoint is (128, 128, 128) — exactly mid grey. The gradient is mathematically correct and looks dead.
Two fixes:
Add an intermediate stop to steer the path through a hue you want:
/* muddy */
linear-gradient(90deg, #0000ff, #ffff00)
/* steered through green */
linear-gradient(90deg, #0000ff, #00c8a0, #ffff00)Or use analogous colours — hues near each other on the wheel. Short interpolation paths cannot pass through grey, which is why violet-to-cyan gradients look professional almost automatically.
Problem two: banding
Banding is visible stripes instead of a smooth blend, most noticeable on large areas with subtle colour changes — a full-page background going from one near-black to another.
The cause is quantisation. Each channel has 256 steps. Spread a 10-value difference across 1400 pixels and you get bands roughly 140 pixels wide, which the eye reads as stripes.
Fixes, in order of how often they work:
- Increase the colour distance. Banding is a symptom of too little change over too much space.
- Add a subtle noise overlay. A tiny amount of visual noise breaks the bands up — this is what print designers have always done.
- Add intermediate stops. Sometimes helps by changing where the steps land.
Modern colour spaces
CSS now lets you specify the interpolation space directly:
linear-gradient(in oklch 90deg, #0000ff, #ffff00)OKLCH is perceptually uniform — equal numeric steps look like equal visual steps — and interpolating in it avoids the grey-midpoint problem entirely, because it travels around the hue circle rather than cutting through the middle of the RGB cube.
Support is good in current browsers but not universal. Treat it as progressive enhancement: specify an sRGB gradient first, then override it for browsers that understand the newer syntax.
Practical recipes
Subtle surface gradient — barely visible, adds depth:
background: linear-gradient(180deg, #1c1c26, #16161f);Accent glow behind a hero section:
background: radial-gradient(ellipse 60% 50% at 50% 30%,
rgba(124,108,255,0.12) 0%, transparent 70%);Scrim over an image, so text stays legible:
background: linear-gradient(180deg,
transparent 0%, rgba(0,0,0,0.75) 100%);That last one is the standard fix for text over photographs, where the contrast ratio otherwise changes across the image.
Gradient text, and animation
Gradient text needs background-clip:
.gradient-text {
background: linear-gradient(135deg, #a78bfa, #38bdf8);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}Include the prefixed property — Safari still needs it.
One accessibility note: gradient text makes contrast hard to evaluate, because the ratio varies along the text. Check the lightest point of the gradient against your background, and keep gradient text for large display headings rather than body copy.
For animation, colour stops are not animatable. The standard workaround is to make the background larger than the element and animate background-position, which produces a smooth travelling effect at no layout cost.
Frequently asked questions
Why does my gradient look grey in the middle?
Browsers interpolate in sRGB, and complementary colours pass through a desaturated midpoint. Add an intermediate colour stop to steer the transition, or use colours closer together on the wheel.
What causes banding in a gradient?
Quantisation — each colour channel has only 256 steps, so a small colour difference spread over a large area produces visible stripes. Increase the colour distance or overlay subtle noise.
Are CSS gradients better than background images?
For solid colour transitions, yes — no HTTP request, no file size, infinitely scalable and easy to theme. Images are still needed for photographic or textured backgrounds.
Can I animate a gradient?
Not the colour stops directly; they are not animatable. Make the background larger than the element and animate background-position instead.
What is OKLCH and should I use it?
A perceptually uniform colour space. Interpolating a gradient in OKLCH avoids the grey-midpoint problem. Support is good in current browsers — use it as progressive enhancement over an sRGB fallback.
Everything on ToolYard runs in your browser. No uploads, no accounts, no limits.
Browse all tools →