How to Make Good CSS Gradients (A Practical Guide)
A CSS gradient is a way to transition smoothly between two or more colors using only CSS — no image files required. You declare it as a value for background or background-image, and the browser renders the blend at any resolution. Getting a gradient to look genuinely polished is trickier than it appears, but once you understand how color stops, angles, and gradient types work together, you can produce professional results in a few lines of code.
The three gradient types and when to use each
CSS provides three gradient functions, each producing a different geometric shape:
| Function | Shape | Typical use |
|---|---|---|
linear-gradient() |
Straight line | Hero backgrounds, button fills, banners |
radial-gradient() |
Circle or ellipse from a center point | Spotlight effects, glows, badge accents |
conic-gradient() |
Sweeps around a center point like a clock hand | Pie charts, color wheels, decorative rings |
For most UI work, linear-gradient() is what you want. Radial and conic gradients are more specialized, but knowing they exist saves you from reaching for an image editor when a CSS function will do the job cleaner.
Linear gradients: angle, stops, and positions
The basic syntax is straightforward:
background: linear-gradient(135deg, #2563eb, #ec4899);
That renders a diagonal blend from blue to pink. The first argument is the angle: 0deg goes bottom-to-top, 90deg goes left-to-right, and 135deg goes top-left to bottom-right. You can also use keywords like to right, to bottom right, and so on in place of degrees.
Adding more color stops
Every value after the angle is a color stop. By default, stops are spaced evenly. You can override the position by adding a percentage:
background: linear-gradient(
to right,
#2563eb 0%,
#7c3aed 40%,
#ec4899 100%
);
This holds the blue longer before sweeping through purple into pink. Controlling stop positions is the biggest lever you have over the perceived shape of a gradient — a stop pushed toward one end creates a fast transition on that side and a slow, drawn-out transition on the other.
Hard stops for crisp edges
If you place two stops at the same position, you get a sharp color boundary with no blend — useful for striped patterns:
background: linear-gradient(
to right,
#2563eb 50%,
#ec4899 50%
);
Combine this with background-size and background-repeat to tile the pattern into stripes.
Radial gradients: shape, size, and position
A radial gradient radiates outward from a focal point. The full syntax gives you control over the shape, how far it extends, and where the center sits:
background: radial-gradient(
circle at 30% 40%,
rgba(37, 99, 235, 0.8) 0%,
transparent 60%
);
This creates a soft circular glow anchored toward the upper-left of the element, fading to transparent. Changing circle to ellipse lets the shape stretch to fill the element naturally, which is the default behavior when you omit the shape keyword.
closest-side — the gradient ends where it first touches an element edge.farthest-corner — the default; the gradient extends to the farthest corner, ensuring full coverage.closest-corner / farthest-side — less common but useful for off-center compositions.You can also pass explicit lengths:
radial-gradient(circle 200px at center, ...).Conic gradients: sweeping around a point
Conic gradients are the newest of the three types and are supported in all modern browsers. The color transitions happen angularly around a center point rather than linearly across the element:
background: conic-gradient(
from 0deg at 50% 50%,
#2563eb 0deg,
#ec4899 180deg,
#2563eb 360deg
);
The from keyword sets the starting angle. Because the gradient wraps around 360 degrees, making the start and end colors match creates a seamless circular sweep — exactly how a color wheel is typically rendered. For a simple pie chart with three segments, you would use hard stops at specific degree values instead of smooth transitions.
Why gradients look muddy — and how to fix it
The most common complaint about CSS gradients is that the midpoint looks gray, dull, or unsaturated — especially with saturated complementary colors like blue and orange. This is a color-space problem: by default, browsers interpolate gradients in the sRGB color space, which passes through a desaturated "dead zone" when colors are opposite on the hue wheel.
Two solutions work well in modern browsers:
- Add a midpoint stop in the right hue. For a blue-to-orange gradient, the midpoint naturally passes through an unsaturated brown. Adding a vivid purple or yellow stop in the middle steers the interpolation away from that dead zone.
- Use the
in oklchinterpolation hint (Chrome 111+, Firefox 113+, Safari 16.2+). OKLCh is a perceptually uniform color space where equal steps in hue look equally different to the human eye:background: linear-gradient(in oklch, #2563eb, #ec4899);
The in oklch approach requires no extra stops and produces visibly more vibrant, even transitions. If you need to support older browsers, the midpoint stop technique is a reliable fallback.
Layering gradients and mixing with images
The background property accepts multiple comma-separated values, and gradients stack like layers — earlier values appear on top:
background:
linear-gradient(to bottom, transparent 60%, rgba(0,0,0,.7)),
url('photo.jpg') center / cover no-repeat;
This overlays a bottom-to-top darkening gradient on top of a photograph, making white text legible at the bottom of a hero image without needing Photoshop. The gradient is defined first so it renders above the image. You can stack as many gradients as you like — this technique is also how CSS mesh gradients are faked: multiple large radial gradients with different colors and positions, blended together over a base color.
Repeating gradients
Each gradient function has a repeating- variant: repeating-linear-gradient(), repeating-radial-gradient(), and repeating-conic-gradient(). These tile the gradient pattern until it fills the element, rather than stretching to cover it once.
background: repeating-linear-gradient(
45deg,
#f1f5f9 0px,
#f1f5f9 10px,
#e2e8f0 10px,
#e2e8f0 20px
);
This produces diagonal stripes without any image or SVG. The key is precise hard stops — the second color must start exactly where the first one ends, and the repeat unit must end exactly where the whole pattern would tile seamlessly.
Practical tips for gradient quality
- Keep stop count low. Two or three stops almost always look cleaner than six. More stops usually means more color management, not more polish.
- Use slight angle offsets. Perfectly horizontal (
90deg) or perfectly vertical (0deg) gradients can look mechanical. A135degor115degdiagonal often feels more natural. - Test on both light and dark backgrounds. A gradient that pops on a white page may flatten out on dark mode. Use semi-transparent stops (
rgbaorhsla) for more flexibility. - Audit contrast on text overlays. If text sits on a gradient, check contrast at both the lightest and darkest parts of the gradient — not just the average. The WCAG 2.1 minimum is 4.5:1 for body text.
- Prefer
background-attachment: fixedsparingly. Parallax gradient effects look impressive but can cause repaints on every scroll frame, hurting performance on low-end devices.
Build your gradient visually
Figro's GradientCraft lets you drag color stops, switch between linear, radial, and conic types, preview on real UI shapes, and copy the finished CSS or Tailwind class — all free, in your browser, no signup.
Open the free gradient generator →Figro's guides are educational and written to be genuinely useful. They are not professional design or development advice. Some pages include affiliate links; if you purchase through them we may earn a commission at no extra cost to you.