Skip to content

CSS Gradient Generator

Make linear, radial and conic gradients: drag colour stops along the bar, set transparency, blend in sRGB or OKLCH, then copy the CSS or a Tailwind class. The text check samples every point of the gradient, not just the stops.

Preview

Sample heading
Body text at 16px needs 4.5:1

1280×720 box · 135° · gradient line 1,414px long, so 1% = 14.1px

Colour stops · click the bar to add, drag to move
0%50%100%
#FFFFFF text: worst point 2.15:1 at 100% along the gradient. 75% of the gradient passes 4.5:1 (green on the strip). Keep body text over that part.
CSS
background-image: linear-gradient(135deg, #1E1B4B 0%, #9D174D 55%, #F59E0B 100%);
Tailwind class
bg-[linear-gradient(135deg,#1E1B4B_0%,#9D174D_55%,#F59E0B_100%)]

An arbitrary value, so it works in Tailwind 3 and 4 with any number of stops. Underscores stand for spaces.

Angles, keywords and the gradient line

A linear gradient runs along an invisible gradient line through the centre of the box. 0deg points up and angles turn clockwise, so 90deg runs left to right and the default, 180deg, runs top to bottom. The line is made just long enough that the corners land exactly on 0% and 100%:

length = |width × sin(angle)| + |height × cos(angle)|

For a 1200×600 hero at 135deg: 1200 × 0.7071 + 600 × 0.7071 = 848.5 + 424.3 = 1,272.8px. A stop at 10% sits 127px along that line, not 120px across the box. The readout under the preview does this sum for the box shape you pick.

Corner keywords are not fixed angles. to top right tilts the line so the top-left and bottom-right corners share the 50% colour, which on a 1200×600 box works out to 26.57° (the arctangent of 600 ÷ 1200), not 45°. Only a square box makes them equal. Use a keyword when the colours should meet the corners on any shape; use an angle when the slant should look the same on every screen.

Linear gradient angles and the keywords that match them
KeywordAngleNotes
to top0degSame on any box
to right90degSame on any box
to bottom180degThe default when you give neither
to left270degSame on any box
to top rightatan(h ÷ w)45° only when square; 26.57° at 2:1
to bottom right180° − atan(h ÷ w)153.43° at 2:1
to bottom left180° + atan(h ÷ w)206.57° at 2:1
to top left360° − atan(h ÷ w)333.43° at 2:1

Colour stops: positions, hard edges, hints

CSS colour stop syntax
You writeWhat happens
red, bluePositions left out are spread evenly: 0% and 100%
red, yellow, blueThe middle stop goes to 50%
red 0 40%, blue 40% 100%A stop with two positions is a solid band; equal positions make a hard edge
red 60%, blue 40%A position smaller than the one before is raised to match it, giving a hard edge at 60%
red, 25%, blueA bare percentage is a hint: the halfway colour moves to 25%
red 20px, blue 200pxLengths work too and stay fixed as the box grows

The generator writes every stop with its position, sorted, so the code means exactly what the bar shows. Two stops on the same percentage make a hard edge; the Pie chart and Stripes presets are built that way. For a repeating gradient, the distance from the first to the last stop is the length of one repeat: stops at 0% and 10% repeat ten times along the gradient line.

Why a gradient goes grey in the middle

With hex, rgb() or hsl() colours, browsers blend each red, green and blue channel separately. Between blue #0000FF and yellow #FFFF00, every channel lands halfway, at 127.5, and the midpoint is plain grey, #808080. Load the "Blue → yellow" preset and switch between sRGB and OKLCH to see the difference.

OKLab is a colour space built so that equal steps look like equal changes. Blending in it, the same midpoint is a soft blue #6CABC7; OKLCH goes round the hue wheel instead, through teal and green, keeping the colours saturated. Ask for it in the gradient itself:

background-image: linear-gradient(90deg, #0000FF, #FFFF00);
background-image: linear-gradient(90deg in oklch, #0000FF, #FFFF00);

Browsers that do not understand in oklch drop the second line and keep the first, which is why the export writes both. The CSS Color 4 specification makes Oklab the default for newer colour formats such as oklch(), but keeps sRGB for hex and rgb() so older pages don't change. With OKLCH, "longer hue" goes the other way round the wheel: red to blue through yellow and green instead of through magenta.

Browser support for gradient features
FeatureChrome / EdgeFirefoxSafari
linear-gradient, radial-gradient26167
Two positions on one stop716412.1
conic-gradient698312.1
in oklab / in oklch11112716.2
Premultiplied alpha (clean fades)293615

Fading to transparent

The keyword transparent is black with zero opacity. Older browsers blended the colour channels and the opacity separately, so a fade from white to transparent went murky grey on the way. Current browsers blend with premultiplied alpha, where a colour's weight shrinks with its opacity, and the grey band is gone; Safari was the last to switch, in version 15.

The generator still writes transparent stops as the same colour at 0% opacity, for example rgb(0 0 0 / 0%). It reads clearly and stays clean everywhere. Set a stop's opacity with the slider, or type an 8-digit hex such as #00000080 (the last two digits are the opacity, 80 = 50%).

The "Fade to transparent" preset is the usual scrim for captions on photos. Its contrast depends on what is underneath, so the checker asks for a backdrop colour. Over a mid-grey photo (#94A3B8), white text gets 13.1:1 at the bottom edge but only meets 4.5:1 for the bottom 38% of the gradient line; the caption has to stay in that part.

Radial and conic options

Radial gradient size keywords
Size keywordWhere 100% ends
farthest-cornerAt the corner farthest from the centre (the default, left out of the code)
farthest-sideAt the farthest edge
closest-cornerAt the nearest corner
closest-sideAt the nearest edge, so the last colour fills the rest

An ellipse stretches with the box; a circle stays round. Moving the centre to an edge, as the Spotlight preset does with at 50% 0%, gives a glow from that edge. Conic gradients sweep round the centre instead of out from it: 0% to 100% is one full turn, which suits pie charts, colour wheels and this checkerboard, often used behind transparent images:

background: repeating-conic-gradient(#ccc 0% 25%, #fff 0% 50%) 0 0 / 20px 20px;

Text on a gradient

WCAG 2 asks for a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text (24px, or 18.66px bold). On a gradient the background changes under every letter, so checking only the stop colours is not enough: the blend between two passing stops can fail, and a failing stop may sit where no text goes. The strip under the bar shows the ratio at 201 points: green for 4.5:1 or more, amber for 3:1 to 4.5:1, red below 3:1.

The Sunset preset shows why that matters. Its stops score 15.99:1, 7.88:1 and 2.15:1 against white, so a stops-only check calls it a fail. Sampled along the gradient, white text stays above 4.5:1 for the first 75% and only drops below 3:1 after about 89%. Keep the heading in the dark three-quarters and it passes; black text only works over the last quarter.

For a radial or conic gradient, 0% is the centre (or start angle) and 100% the outer edge (or the full turn), so read the strip that way.

Banding in subtle gradients

Most screens show 256 levels per colour channel. A dark gradient from #1E293B to #0F172A moves red from 30 to 15, green from 41 to 23 and blue from 59 to 42, so the colour can only change about 50 times. Across a 1,000px hero that leaves steps roughly 20px wide, and on a good monitor they show as faint stripes.

Ways round it: shorten the distance or widen the colour range, blend in OKLab, or lay a very faint noise texture over the gradient. Some browsers dither gradients, which hides part of the effect, but you cannot rely on it everywhere.

Animating a gradient

A transition on background-image jumps straight to the new gradient, because browsers cannot interpolate one image into another. Register the part that should change as a typed custom property and animate that instead:

@property --angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 135deg;
}

.card {
  background-image: linear-gradient(var(--angle), #1E1B4B, #F59E0B);
  transition: --angle 0.6s;
}

.card:hover { --angle: 315deg; }

@property works in Chrome 85, Safari 16.4 and Firefox 128 onwards. Colours can be animated the same way with syntax: '<color>'. The older trick of a large background-size with a moving background-position still works everywhere.

Frequently asked questions

How do I put a gradient on text?

Set the gradient as background-image on the text element, then add background-clip: text and color: transparent. Keep -webkit-background-clip: text alongside it for older Safari and Chrome versions.

Why does my page gradient repeat in bands?

A gradient on body is painted across the whole window but sized to the html element, which is only as tall as the content, so it tiles down the page. Give html a min-height of 100%, or set background-repeat: no-repeat with a background-color for the rest.

Can I layer several gradients?

Yes. background-image takes a comma-separated list, and the first one listed is drawn on top. Give the upper layers transparent parts so the ones below show through.

How do I make a gradient border?

Use border-image: linear-gradient(…) 1 for square corners. For rounded corners, set a transparent border and two backgrounds: the fill clipped to padding-box and the gradient to border-box.

Do gradients need -webkit- prefixes?

No. Unprefixed linear and radial gradients have worked in every major browser for over a decade. The old -webkit-gradient() syntax is only needed for browsers nobody supports.

Is a CSS gradient faster than an image?

Usually. There is no file to download and it stays sharp at any size. Very large gradients that are repainted on every frame, for example during an animation, can cost more than a cached image.

Sources