CSS Gradient Generator
CSS gradients are generated images, which is why they scale without artefacts and cost no HTTP request. Two things about them are consistently surprising: the angle convention, and the fact that interpolating between two saturated colours can produce a muddy grey in the middle.
How to use it
- Choose linear or radial and set your colour stops.
- Adjust the angle or shape and watch the preview.
- Copy the generated CSS.
The angle convention is not the one you expect
In CSS gradients, 0deg points to the top and angles increase clockwise. So 90deg points right, 180deg points down, and 270deg points left.
This differs from the mathematical convention, where 0 points right and angles increase anticlockwise, and it differs from CSS transform rotation, which starts from a different reference. Getting a gradient running the opposite way to what you intended is nearly always this.
The keyword forms avoid the problem entirely and read better: to bottom, to right, to bottom right. Note that to bottom right is not the same as 135deg unless the element happens to be square, because the keyword form points at the actual corner and adjusts for the aspect ratio.
The grey dead zone
Interpolating between two saturated colours of different hues frequently produces a desaturated, muddy band in the middle. Blue to yellow gives grey. Red to green gives brown.
The cause is that CSS interpolates in sRGB by default, blending the red, green, and blue channels independently. Halfway between pure blue and pure yellow, all three channels land near the middle of their range, which is grey. The maths is doing exactly what it was asked; the problem is that channel-wise averaging does not correspond to how we perceive colour mixing.
Two fixes. The simple one is to add an intermediate colour stop with the hue and saturation you actually want in the middle, which forces the gradient through it. The better one, where support allows, is to interpolate in a perceptually uniform space: linear-gradient(in oklab, ...) or in oklch keeps saturation through the transition, and the oklch form additionally lets you specify whether the hue travels the short or long way around the wheel.
Banding, and why it is worse than it used to be
A gradient across a large area with 8-bit colour has only 256 levels per channel to work with. Spread a subtle transition across 1,500 pixels and you get visible steps, which show up as bands.
Wide, low-contrast gradients are the worst case, because a small colour difference stretched over a large distance gives each step more pixels to occupy. Dark gradients are also worse, because sRGB allocates fewer distinguishable levels at the low end.
Mitigations, roughly in order of effort: increase the contrast between the stops so the steps are proportionally smaller; add intermediate stops to control where the transition happens; overlay a very subtle noise texture, which is what dithering does and what most design tools do automatically when exporting; or reduce the physical area the gradient covers.
Practical notes
Avoid the transparent keyword in a gradient stop. It resolves to rgba(0,0,0,0), which is transparent black, so a gradient from red to transparent passes through dark, muddy semi-transparent reds. Write rgba with the same colour and zero alpha instead, and the fade stays clean. Browsers have improved here but the explicit form is unambiguous.
Gradients are images, not colours, so they apply to background-image rather than background-color. To put a gradient on text, apply it as a background and use background-clip: text with a transparent text colour.
Multiple gradients can be layered on one element with commas, first on top. Combined with transparency this is how mesh-like and noise effects are built without any image files. Each layer costs paint time, so a few is fine and a dozen on a scrolling element is not.
At a glance
| Angle convention | 0deg points up, increasing clockwise |
|---|---|
| Default interpolation | sRGB, channel-wise |
| Perceptual option | in oklab or in oklch |
| Transmitted | Nothing |
Frequently asked questions
Why is my gradient running the wrong way?
The CSS convention is 0deg pointing up and increasing clockwise, which differs from the mathematical convention. Use keyword directions such as to right if the angle keeps confusing you.
Why is there a grey band in the middle?
Default sRGB interpolation averages the channels independently, and the midpoint between two different saturated hues lands near grey. Add an intermediate stop, or interpolate in oklab or oklch.
How do I fix visible banding?
Increase contrast between stops, add intermediate stops, reduce the area, or overlay a subtle noise texture. Banding comes from having only 256 levels per channel across a large distance.
Why does fading to transparent look dirty?
The transparent keyword means transparent black, so the gradient passes through dark tones. Use rgba with your own colour and zero alpha instead.