Vorinda
CSS··13 min read

CSS Gradients: How to Create Linear, Radial & Conic Gradients

Learn to create linear, radial and conic CSS gradients — syntax, multi-stop recipes, modern OKLCH interpolation and copy-paste examples.

Hasan Afzal
cssgradientsweb designtutorials
On this page

A CSS gradient is a smooth blend between two or more colors that you can drop into almost any background. The three functions — linear-gradient(), radial-gradient() and conic-gradient() — cover everything from a subtle hero wash to a full color wheel. This guide is a practical, copy-paste tour: the syntax that actually matters, the modern OKLCH tricks that fix muddy blends, and recipes for gradient text, borders and Tailwind.

CSS gradients are images, not colors

Here is the one idea that makes everything else click: a gradient is an image, not a color. linear-gradient(...) produces a <gradient>, which is a kind of <image> — the same type as a JPEG or an SVG. That single fact explains most of the gotchas you will ever hit.

Because a gradient is an image:

  • It belongs anywhere an image is allowed — background-image, border-image, mask-image, even list-style-image — and not anywhere a plain color is required, like color or border-color.
  • It has no fixed pixel size. Gradients are resolution-independent and stretch to fill their box without blurring, so you size and position them with background-size, background-position and background-repeat.
  • You can stack several in one background declaration, comma-separated, where earlier layers paint on top.
.box {
  /* shorthand: the gradient is the background image */
  background: linear-gradient(90deg, #3b82f6, #ec4899);
}

This is also why gradient text and gradient borders need a trick later on — the text fill and the border line both expect a color, so you have to route the gradient image through background-clip or border-image. Keep "it's an image" in your back pocket and the rest is detail.

Linear gradients

linear-gradient() blends colors along a straight line. The first argument is the direction; everything after is a list of color stops.

.hero {
  background: linear-gradient(to right, #3b82f6, #8b5cf6);
}

Direction: keywords vs angles

You can aim a linear gradient with a keyword (to right, to bottom, to top left) or an angle. The angle is measured clockwise from pointing up, which trips people up — 0deg goes upward, not rightward.

DirectionAngleGoes
to top0degbottom → top
to right90degleft → right
to bottom (default)180degtop → bottom
to left270degright → left
to top rightcorner-awaretoward the upper-right corner

If you omit the direction entirely, the gradient runs top to bottom (180deg). Corner keywords like to top right are special: the gradient line is angled so its colors land exactly on that corner regardless of the box's aspect ratio, so it is rarely exactly 45deg.

Multi-stop gradients

Add more colors for a multi-stop blend. By default they are spaced evenly, but you can pin any stop to a position with a length or percentage.

.sunset {
  background: linear-gradient(
    120deg,
    #facc15 0%,
    #f97316 40%,
    #ec4899 100%
  );
}

Two quick rules worth memorizing: if a stop's position is smaller than the one before it, the browser clamps it up to match (handy for hard stops, below), and a stop with no position simply sits halfway between its neighbors.

Radial gradients

radial-gradient() radiates outward from a center point. The optional first part describes the shape, size and position; the rest is the same color-stop list.

.spotlight {
  background: radial-gradient(circle at center, #8b5cf6, #1e293b);
}

Shape and size

The shape is either circle or ellipse (the default). The size controls where the final color stop lands, using one of four keywords or explicit lengths.

Size keywordEnds at…
closest-sidethe nearest edge of the box
closest-cornerthe nearest corner
farthest-sidethe farthest edge
farthest-corner (default)the farthest corner
/* a 200px circle offset toward the top-right */
.glow {
  background: radial-gradient(circle 200px at 70% 30%, #06b6d4, transparent);
}

The at <position> clause accepts the same values as background-position (center, top left, 70% 30%, 20px 40px). Offsetting the center is how you fake a light source or a soft vignette. Fade the last stop to transparent and a radial gradient becomes a reusable glow you can layer over anything.

Conic gradients

A conic-gradient() sweeps colors around the center point like the hand of a clock, instead of toward or away from it. Its color stops are measured in angles (degrees or turns), not lengths.

.wheel {
  background: conic-gradient(from 0deg, #3b82f6, #8b5cf6, #ec4899, #3b82f6);
}

from <angle> rotates the starting position (default 0deg points straight up), and at <position> moves the center, exactly like radial gradients.

Pie charts with hard stops

Give two stops the same angle and the color changes instantly — perfect for pie and donut charts with zero JavaScript.

.pie {
  border-radius: 50%;
  background: conic-gradient(
    #3b82f6 0deg 90deg,    /* 25% */
    #22c55e 90deg 200deg,  /* ~30% */
    #f97316 200deg 280deg, /* ~22% */
    #facc15 280deg 360deg  /* ~23% */
  );
}

Color wheels and patterns

Listing the full hue circle and looping back to the first color produces a genuine color wheel — great for pickers and decorative dials.

.color-wheel {
  border-radius: 50%;
  background: conic-gradient(red, yellow, lime, cyan, blue, magenta, red);
}

repeating-conic-gradient() is the secret behind the Photoshop "transparent" checkerboard. One tile holds four alternating quadrants; tiling it with background-size produces a seamless board:

.checker {
  background: conic-gradient(#ccc 25%, #fff 0 50%, #ccc 0 75%, #fff 0);
  background-size: 40px 40px;
}

Notice the 0 shorthand — because 0 is smaller than the previous stop, it clamps up to it, a concise way to write back-to-back hard stops.

Color stops, hints & hard stops

Stops, hints and hard stops are shared across all three gradient types, so it is worth understanding them once.

A hard stop is two stops at the same position, giving a crisp line instead of a blend. Use it for stripes, progress bars and flag-like blocks:

/* two solid halves, no blend */
.split {
  background: linear-gradient(90deg, #3b82f6 0 50%, #8b5cf6 50% 100%);
}

A color hint is a lone percentage between two color stops. It does not add a color — it moves the midpoint of the blend, letting you bias a gradient toward one end:

/* the 50/50 mix happens at 30%, so it skews dark early */
.fade {
  background: linear-gradient(#0f172a, 30%, #3b82f6);
}

For repeating patterns — barber poles, rulers, hazard stripes — reach for repeating-linear-gradient(). The stop positions define one tile that repeats forever:

.stripes {
  background: repeating-linear-gradient(
    45deg,
    #3b82f6 0 12px,
    #8b5cf6 12px 24px
  );
}

Need a starting palette for any of these? Generate a coherent set with the Color Palette Generator, then pull individual values with the Color Picker.

Modern gradients: OKLCH interpolation & color-mix()

Here is a problem that has quietly haunted CSS for years. Blend blue into yellow in the default sRGB space and the middle turns gray:

/* midpoint is a muddy #808080 */
.muddy {
  background: linear-gradient(to right, blue, yellow);
}

That happens because straight-line interpolation through sRGB passes through the desaturated center of the color cube. The fix is to interpolate in a perceptual space by adding an in <color-space> clause:

/* midpoint stays a vivid teal — no gray dead zone */
.clean {
  background: linear-gradient(in oklch, blue, yellow);
}

OKLCH keeps lightness perceptually even and routes the hue around the color wheel instead of cutting through gray. For cylindrical spaces like oklch, lch and hsl you can even pick which way around the wheel to travel:

.spectrum {
  background: linear-gradient(in oklch longer hue, #3b82f6, #ec4899);
}

The hue interpolation methods are shorter hue (default), longer hue, increasing hue and decreasing huelonger hue is the easy way to get a rainbow from just two stops. If OKLCH is new to you, the OKLCH explainer covers why it beats HSL for picking and blending color.

color-mix() for stops you compute

color-mix() blends two colors in a chosen space and returns a single color — ideal for deriving gradient stops from one brand value instead of hand-tuning hexes:

:root { --brand: #3b82f6; }

.card {
  background: linear-gradient(
    135deg,
    color-mix(in oklch, var(--brand) 85%, white),
    var(--brand),
    color-mix(in oklch, var(--brand) 80%, black)
  );
}

Change --brand and the whole ramp re-derives itself. Mixing toward white and black like this is exactly how tints and shades work — the Shade & Tint Generator does it visually if you would rather copy finished values. in oklch and color-mix() are supported across current Chrome, Safari and Firefox.

Layering & transparency

Because gradients are images, you can stack several in one background, and you can fade any stop to transparent. That unlocks overlays, scrims and glass effects.

A scrim darkens a photo so text stays readable. Put a semi-transparent gradient above the image (earlier layers win):

.hero {
  background:
    linear-gradient(rgba(15, 23, 42, 0.7), rgba(15, 23, 42, 0.3)),
    url("/hero.jpg");
  background-size: cover;
}

Layered radial gradients over a dark base fake the trendy mesh gradient look:

.mesh {
  background:
    radial-gradient(at 20% 20%, #8b5cf6 0, transparent 50%),
    radial-gradient(at 80% 0%, #3b82f6 0, transparent 50%),
    radial-gradient(at 0% 80%, #ec4899 0, transparent 50%),
    #0f172a;
}

For glassmorphism, combine a faint white gradient with backdrop-filter:

.glass {
  background: linear-gradient(
    135deg,
    rgba(255, 255, 255, 0.25),
    rgba(255, 255, 255, 0.05)
  );
  backdrop-filter: blur(12px);
  border: 1px solid rgba(255, 255, 255, 0.2);
}

When you fade to transparent, prefer the same color at zero alpha (rgba(15,23,42,0)) over the keyword transparent if you ever see a gray haze — transparent is shorthand for transparent black, which can tint the blend in sRGB.

Gradient text & borders

Both effects exist only because a gradient is an image and text/borders want a color. The workaround is to clip or border-image the gradient.

Gradient text clips a background to the shape of the glyphs and makes the real fill transparent:

.gradient-text {
  background: linear-gradient(90deg, #8b5cf6, #ec4899);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent; /* fallback */
}

Keep gradient text to large, bold headings — thin glyphs reduce contrast and hurt legibility.

Gradient borders have two good approaches. border-image is one line but cannot follow border-radius:

.border-1 {
  border: 4px solid;
  border-image: linear-gradient(90deg, #3b82f6, #ec4899) 1;
}

When you need rounded corners, stack two backgrounds with different clip boxes — a solid fill on padding-box over a gradient on border-box:

.border-2 {
  border: 2px solid transparent;
  border-radius: 12px;
  background:
    linear-gradient(#fff, #fff) padding-box,
    linear-gradient(90deg, #3b82f6, #ec4899) border-box;
}

Gradients in Tailwind CSS

Tailwind v4 renamed the gradient utilities. The old bg-gradient-to-* classes from v3 became bg-linear-*, and bg-radial / bg-conic are now first-class.

<!-- linear, left to right -->
<div class="bg-linear-to-r from-blue-500 via-purple-500 to-pink-500"></div>

<!-- arbitrary angle -->
<div class="bg-linear-45 from-cyan-500 to-blue-600"></div>

<!-- radial and conic -->
<div class="bg-radial from-violet-500 to-slate-900"></div>
<div class="bg-conic from-blue-500 via-pink-500 to-blue-500"></div>

Those color names map to real hexes: from-blue-500 is #3b82f6, via-purple-500 is #a855f7, to-pink-500 is #ec4899. Position the stops with from-10%, via-40%, to-90%, and switch interpolation with a slash modifier — bg-linear-to-r/oklch matches the in oklch trick from earlier, while /longer takes the long way around the hue wheel.

<div class="bg-linear-to-r/oklch from-blue-500 to-yellow-400">
  smooth, no gray midpoint
</div>

Tailwind defaults to sRGB interpolation, so adding /oklch is the single most impactful upgrade for any multi-color gradient.

Animating gradients

You cannot transition a gradient directly — it is an image, and there is nothing meaningful to interpolate between two images. Two workarounds get you there.

The compatible trick oversizes the gradient and slides it. Set a background-size larger than the box and animate background-position:

.animated-gradient {
  background: linear-gradient(270deg, #3b82f6, #8b5cf6, #ec4899);
  background-size: 600% 600%;
  animation: drift 8s ease infinite;
}
@keyframes drift {
  0%, 100% { background-position: 0% 50%; }
  50%      { background-position: 100% 50%; }
}

The modern trick registers a custom property with @property so the browser knows its type and can interpolate it. Animate the property and the gradient follows:

@property --stop {
  syntax: "<color>";
  inherits: false;
  initial-value: #3b82f6;
}
.button {
  background: linear-gradient(90deg, var(--stop), #ec4899);
  transition: --stop 0.4s ease;
}
.button:hover { --stop: #22c55e; }

The same pattern with syntax: "<angle>" animates a conic gradient's from angle — the basis of the spinning-glow border you see on so many landing pages. @property works in current Chrome, Edge, Safari and Firefox; just wrap any animation in the prefers-reduced-motion guard from the next section.

Performance, accessibility & fallbacks

Gradients are cheap to paint, but a few habits keep them fast and inclusive.

Performance. Static gradients are essentially free. The cost shows up when you animate them: the common background-position technique repaints every frame and is not GPU-composited, so keep animated gradients off huge full-page elements, and animate transform/opacity instead where you can. Large layered radial and conic gradients are heavier than a single linear one.

Accessibility. Text over a gradient must clear WCAG contrast against the worst-case stop, not the average — check both the lightest and darkest points with the Contrast Checker. And always respect motion preferences:

@media (prefers-reduced-motion: reduce) {
  .animated-gradient { animation: none; }
}

Fallbacks. Declare a solid color first so any browser that fails to parse the gradient still gets a sensible background — the cascade naturally uses the last declaration it understands:

.hero {
  background: #6d28d9;                          /* fallback */
  background: linear-gradient(in oklch, #3b82f6, #ec4899);
}

The same pattern covers OKLCH: list an sRGB gradient first, then the in oklch version. For dark UIs, the dark mode palette guide shows how to keep gradient backgrounds legible in both themes, and color schemes explained helps you choose stops that actually harmonize.

When you would rather drag sliders than hand-write stops, the CSS Gradient Generator outputs all three gradient types — including OKLCH interpolation — as copy-paste CSS. Pair it with the blue and purple color pages for ready-made stop values.

Frequently asked questions

How do I create a CSS gradient?

Set an element's background to a gradient function such as linear-gradient(), radial-gradient() or conic-gradient(), then list two or more colors, e.g. background: linear-gradient(90deg, #3b82f6, #ec4899). Gradients are images, so they belong on background, not on the color property.

What's the difference between linear and radial gradients?

A linear gradient blends colors along a straight line in one direction set by an angle or keyword like to right. A radial gradient blends outward from a center point as a circle or ellipse. A conic gradient sweeps colors around the center like a clock hand.

How do I make a conic gradient?

Use conic-gradient() with optional from <angle> and at <position>, then color stops measured in degrees, e.g. conic-gradient(from 0deg, red, yellow, lime, blue, red). Equal hard stops turn it into a pie chart, and repeating-conic-gradient() makes starbursts.

Can I animate a CSS gradient?

Yes. The most compatible trick is to set a large background-size and animate background-position. The modern way is to register a color or angle with @property and transition that custom property, which lets the gradient itself interpolate smoothly.

How do I add a gradient in Tailwind?

In Tailwind v4 use a direction utility plus color stops: bg-linear-to-r from-blue-500 via-purple-500 to-pink-500. Add bg-radial or bg-conic for other types, and a modifier like /oklch (bg-linear-to-r/oklch) to control interpolation.