Gradients
A gradient is a smooth change from one colour to another. Buttons, hero sections, progress bars, pie charts and even ruled notebook paper can all be drawn with gradients, with no image file to download and no blurry edges when the screen is zoomed.
In this lesson you will learn the three kinds of gradient, how colour stops and hints control the blend, how to repeat a gradient to make patterns, how to get brighter blends with oklch, and two popular tricks: gradient text and gradient borders. How to stack gradients with photos is covered later in Backgrounds & Gradients.
A gradient is an image
This is the first thing to understand: in CSS a gradient is an image, not a colour. The browser draws it at whatever size the element has. So it goes where an image goes, usually background-image or the background shorthand.
.banner {
background-image: linear-gradient(#ffedd5, #fed7aa);
}
/* Wrong: background-color only accepts a single colour, so this line is ignored */
.wrong {
background-color: linear-gradient(#ffedd5, #fed7aa);
}Because it is an image, a gradient can also be used in border-image, mask-image and list-style-image. You will meet masks in Clip-path, Shapes & Masks.
Linear gradients
linear-gradient() blends colours along a straight line. With no direction it runs from top to bottom. You can name a direction with to and a side or corner, or give an angle:
to right,to top,to bottom left: the side or corner the gradient travels towards.0degpoints up,90degpoints right,180degpoints down (the default).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Linear gradients</title>
<style>
body { font-family: system-ui, sans-serif; padding: 1rem; display: flex; flex-wrap: wrap; gap: 1rem; }
.box { width: 10rem; height: 7rem; border-radius: 10px; display: grid; place-content: center; font-weight: 600; }
.a { background-image: linear-gradient(#bae6fd, #0369a1); }
.b { background-image: linear-gradient(to right, #ff9933, white, #138808); }
.c { background-image: linear-gradient(135deg, #fde68a, #f97316, #be123c); }
.d { background-image: linear-gradient(to top right, #1e3a8a, #93c5fd); color: white; }
</style>
</head>
<body>
<div class="box a">Default</div>
<div class="box b">to right</div>
<div class="box c">135deg</div>
<div class="box d">to top right</div>
</body>
</html>Change 135deg to 45deg and run it again to see the line turn. Add more colours and the browser spreads them out evenly.
Colour stops and hints
Each colour in the list is a colour stop. You can say exactly where a stop sits with a length or percentage after the colour. The browser blends smoothly between neighbouring stops.
#f97316 30%puts that colour at 30% of the line.#f97316 30% 60%(two positions) holds the colour solid from 30% to 60%.- Two stops at the same position make a hard edge with no blend at all.
A colour hint is a bare position between two stops, with no colour. It moves the midpoint of the blend. Normally the half-way mix sits exactly between the stops; a hint of 20% pulls it to 20%, so the second colour takes over sooner.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Stops and hints</title>
<style>
body { font-family: system-ui, sans-serif; padding: 1rem; }
.bar { height: 3rem; margin-block: 0.4rem 1.2rem; border-radius: 6px; }
.even { background: linear-gradient(to right, #fde047, #7c3aed); }
.hint { background: linear-gradient(to right, #fde047, 20%, #7c3aed); }
.solid { background: linear-gradient(to right, #fde047 0 30%, #7c3aed 70% 100%); }
.hard { background: linear-gradient(to right, #fde047 50%, #7c3aed 50%); }
</style>
</head>
<body>
<p>Two stops, blended evenly</p> <div class="bar even"></div>
<p>A hint at 20% moves the midpoint</p> <div class="bar hint"></div>
<p>Solid from 0 to 30% and 70% to 100%</p> <div class="bar solid"></div>
<p>Same position: a hard edge</p> <div class="bar hard"></div>
</body>
</html>Compare the second bar with the first: the purple arrives much earlier, but the start and end colours are the same. The third bar blends only in the middle 40%. The last bar is a sharp split, which is the basis of stripes and flags.
Radial gradients
radial-gradient() spreads colours outwards from a centre. The default shape is an ellipse that fits the box, centred, and reaching the farthest corner. You can change all of that:
.spotlight {
/* shape, size and position come first, then the stops */
background: radial-gradient(circle at 30% 25%, #fef9c3, #f59e0b 40%, #78350f);
}
.glow {
/* closest-side: the circle stops at the nearest edge of the box */
background: radial-gradient(circle closest-side, #a5f3fc, #0e7490);
}The size keywords are closest-side, closest-corner, farthest-side and farthest-corner (the default). You can also give an exact size, such as circle 80px.
Conic gradients
conic-gradient() sweeps colours around a centre point, like the hand of a clock. It starts at the top and turns clockwise. Use from to rotate the start and at to move the centre.
.pie {
border-radius: 50%;
/* Rohan's monthly budget: rent 45%, food 30%, travel 15%, savings 10% */
background: conic-gradient(#2563eb 0 45%, #16a34a 0 75%, #f59e0b 0 90%, #db2777 0);
}
.wheel {
border-radius: 50%;
background: conic-gradient(from 90deg, red, yellow, lime, aqua, blue, magenta, red);
}A stop position of 0 means "start where the previous colour ended". That gives hard edges between slices without repeating every number.
Repeating gradients
repeating-linear-gradient(), repeating-radial-gradient() and repeating-conic-gradient() take the stops you give and repeat them until the box is full. The last stop position sets the length of one repeat. This is how you draw stripes, rings and checkerboards.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Repeating gradients</title>
<style>
body { font-family: system-ui, sans-serif; padding: 1rem; display: flex; flex-wrap: wrap; gap: 1rem; }
.notebook {
width: 18rem;
padding: 0 1rem 0 3.5rem;
line-height: 1.75rem;
background:
linear-gradient(to right, transparent 2.8rem, #ef4444 2.8rem 2.9rem, transparent 2.9rem),
repeating-linear-gradient(white 0 calc(1.75rem - 1px), #93c5fd calc(1.75rem - 1px) 1.75rem);
}
.stripes {
width: 10rem; height: 1.5rem; border-radius: 999px;
background: repeating-linear-gradient(-45deg, #22c55e 0 10px, #16a34a 10px 20px);
}
.checker {
width: 8rem; height: 8rem;
background: repeating-conic-gradient(#e2e8f0 0 25%, white 0 50%) 0 0 / 2rem 2rem;
}
</style>
</head>
<body>
<p class="notebook">Class 7 homework, Meera: write ten lines about your favourite festival and why your family enjoys it so much every year.</p>
<div>
<div class="stripes"></div>
<p>Upload progress</p>
<div class="checker"></div>
</div>
</body>
</html>The notebook uses two layers: a thin red margin line on top and repeating blue rules below. Each rule repeats every 1.75rem, the same as the line-height, so the writing sits on the lines. The checkerboard repeats a four-slice conic gradient inside each 2rem tile.
Brighter blends with oklch
Blend two strong colours, such as blue and orange, and the middle often turns a muddy grey. That happens because gradients between ordinary hex or rgb() colours blend in the sRGB colour space by default. You can choose a different interpolation colour space right after the direction:
.button {
background: linear-gradient(to right, #2563eb, #f97316); /* fallback */
background: linear-gradient(to right in oklch, #2563eb, #f97316); /* livelier middle */
}
.rainbow {
/* same start and end colour, but go the long way round the colour wheel */
background: linear-gradient(to right in oklch longer hue, oklch(70% 0.18 30), oklch(70% 0.18 30));
}in oklch and in oklab blend the way our eyes see colour (see CSS Colours). With oklch, you can also choose how the hue travels: shorter hue (the default) or longer hue, which passes through every colour of the rainbow. This syntax works in current versions of all major browsers. Older browsers drop the whole declaration, so keep the plain gradient on the line before as a fallback.
Gradient text
To fill letters with a gradient, paint the gradient as a background, then clip the background to the shape of the text and make the text colour transparent:
.headline {
color: #7c3aed; /* solid fallback */
width: fit-content; /* the gradient spans the text, not the whole row */
}
@supports (background-clip: text) or (-webkit-background-clip: text) {
.headline {
background: linear-gradient(90deg in oklch, #f97316, #db2777, #7c3aed);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
}The unprefixed background-clip: text works in current browsers, but older Chrome and Safari versions needed the -webkit- form, so many sites write both. The @supports check, explained in @supports Feature Queries, makes sure the text never becomes invisible in a browser that cannot clip. Check that every part of the gradient has enough contrast with the page background.
Gradient borders
border-color only takes a single colour. To get a gradient border that also follows rounded corners, give the element a transparent border and two background layers: a plain one clipped to the padding box on top, and the gradient on the full border box underneath.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Gradient border and text</title>
<style>
body { font-family: system-ui, sans-serif; padding: 1.5rem; background: #f8fafc; }
.plan {
max-width: 18rem;
padding: 1.25rem;
border: 3px solid transparent;
border-radius: 16px;
background:
linear-gradient(white, white) padding-box,
linear-gradient(135deg in oklch, #f97316, #db2777, #7c3aed) border-box;
}
.plan h2 { margin: 0 0 0.5rem; width: fit-content; color: #7c3aed; }
@supports (background-clip: text) or (-webkit-background-clip: text) {
.plan h2 {
background: linear-gradient(90deg in oklch, #f97316, #7c3aed);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
}
</style>
</head>
<body>
<article class="plan">
<h2>Pro plan</h2>
<p>Unlimited mock tests for Class 12 board exams, ₹1,299 per year.</p>
</article>
</body>
</html>The white layer hides the gradient everywhere except the 3px border ring, and both layers follow border-radius.
Animating a gradient
A transition from one gradient to another does not animate: the browser simply swaps the images. To animate a gradient, register a custom property with @property (see Custom Properties) and animate that value instead:
@property --angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.live-badge {
background: conic-gradient(from var(--angle), #f97316, #7c3aed, #f97316);
animation: turn 3s linear infinite;
}
@keyframes turn {
to { --angle: 360deg; }
}
@media (prefers-reduced-motion: reduce) {
.live-badge { animation: none; }
}Because the browser knows --angle is an angle, it can work out every value in between, and the colours rotate smoothly.
Common mistakes
- Putting a gradient in
background-color. It is an image; usebackground-imageorbackground. - Writing
backgroundafterbackground-clip. The shorthand resets the clip to its default. Put the shorthand first. - Expecting
90degto point down. Angles start at the top and turn clockwise, so90degpoints right. - Invisible gradient text. Without a fallback,
color: transparenthides the text where clipping is not supported. - Low contrast on part of the gradient. Text that is readable on the dark end may vanish on the light end.
Key points
- Gradients are images drawn by the browser; use them in
background-imageorbackground. linear-gradient()follows a line,radial-gradient()spreads from a centre,conic-gradient()sweeps around a point.- Stop positions control where colours sit; equal positions make hard edges, and a colour hint moves the midpoint.
repeating-*gradients repeat a pattern for stripes, rules, rings and checkerboards.- Add
in oklchfor livelier blends, with a plain gradient as a fallback line. - Gradient text uses
background-clip: text; gradient borders usepadding-boxandborder-boxbackground layers.
Exercise
Make a "Diwali Sale" banner for a Pune electronics shop:
- a background that blends from deep purple to orange using
in oklch, with a plain fallback line; - a heading with gradient text and a solid fallback colour;
- a "Shop now" link inside a box with a 2px gradient border and rounded corners.
Show solution
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Diwali Sale</title>
<style>
body { font-family: system-ui, sans-serif; margin: 0; }
.banner {
padding: 2.5rem 1.5rem;
background: linear-gradient(120deg, #4c1d95, #f97316);
background: linear-gradient(120deg in oklch, #4c1d95, #f97316);
}
h1 { margin: 0 0 1rem; font-size: 2.5rem; width: fit-content; color: #fef3c7; }
@supports (background-clip: text) or (-webkit-background-clip: text) {
h1 {
background: linear-gradient(90deg, #fef3c7, #fde047);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
}
.cta {
display: inline-block;
padding: 0.6rem 1.2rem;
border: 2px solid transparent;
border-radius: 999px;
color: #4c1d95;
font-weight: 700;
text-decoration: none;
background:
linear-gradient(white, white) padding-box,
linear-gradient(90deg, #fde047, #db2777) border-box;
}
</style>
</head>
<body>
<section class="banner">
<h1>Diwali Sale</h1>
<a class="cta" href="#offers">Shop now</a>
</section>
</body>
</html>Each modern feature has a fallback: the plain gradient line before the in oklch line, and the solid heading colour outside @supports. The link's white layer covers the padding box, so the gradient shows only in the transparent border.
Quick Quiz
1. Why is background-color: linear-gradient(red, blue); ignored?
Gradients are generated images, so they belong in background-image or background.
2. What does linear-gradient(to right, gold 50%, navy 50%) draw?
Two stops at the same position meet with a hard edge and no blending.
3. In linear-gradient(red, 20%, blue), what is the 20%?
A bare position between two stops is a colour hint: it decides where the half-way colour appears.
4. Which technique gives a gradient border that follows border-radius?
border-image ignores rounded corners, while background layers follow them.
Learn CSS with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
