What is a CSS radial gradient?

A radial gradient is a smooth color transition that radiates outward from a center point. Think of it like a spotlight, a ripple, or the soft edge of a bubble. In CSS, radial gradients are created with radial-gradient() (or its repeating sibling repeating-radial-gradient()), and they’re most commonly used as background images.

Two key ideas to keep in mind:

  • Gradients are images. That means they go in background-image (or mask-image), can be layered, and can be positioned and sized like images.
  • Radial gradients have a “shape”, a “size”, and a “position”. Once you understand those three knobs, you can make basically any effect.
.demo {
  background-image: radial-gradient(#ff3d71, #1c1c1c);
}
  
.demo {
  background-image: radial-gradient(#00c2ff 0%, #00ffa3 45%, #1c1c1c 100%);
}
  
.demo {
  background-image: radial-gradient(#ffd166 0 35%, #1c1c1c 35% 100%);
}
  
.demo {
  background-image: repeating-radial-gradient(#ffffff 0 8px, #1c1c1c 8px 16px);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  font-family: system-ui, Arial, sans-serif;
  padding: 18px;
  max-width: 980px;
  display: grid;
  gap: 14px;
}

.demo {
  height: 320px;
  border-radius: 18px;
  border: 3px solid #111;
  display: grid;
  place-items: center;
  color: #fff;
  padding: 16px;
}

.demo strong {
  background: rgba(0, 0, 0, 0.35);
  padding: 8px 12px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.25);
}

.note {
  border: 2px dashed #111;
  border-radius: 14px;
  padding: 12px;
  background: #fff;
}
  
radial-gradient()

In these snippets, the gradient is the background image. Color stops like 45% control where colors blend or “snap”.

CSS radial gradient background

The most common use is a CSS radial gradient background. The simplest form is:

background-image: radial-gradient(color1, color2);

But the real fun starts when you layer multiple gradients, or when you control the shape, position, and size.

.card {
  background-image: radial-gradient(#ff3d71, #151515);
}
  
.card {
  background-image:
    radial-gradient(#00c2ff 0%, transparent 55%),
    radial-gradient(#00ffa3 0%, transparent 55%);
}
  
.card {
  background-image:
    radial-gradient(#00c2ff 0%, transparent 60%),
    radial-gradient(#ff3d71 0%, transparent 60%),
    radial-gradient(#ffd166 0%, transparent 60%);
}
  
.card {
  background-image:
    radial-gradient(#ffd166 0 35%, transparent 35% 100%),
    linear-gradient(135deg, #111, #2a2a2a);
}
  
.card {
  background-image:
    repeating-radial-gradient(rgba(255, 255, 255, 0.14) 0 6px, transparent 6px 18px),
    linear-gradient(135deg, #111, #2a2a2a);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  font-family: system-ui, Arial, sans-serif;
  padding: 18px;
  max-width: 980px;
  display: grid;
  gap: 14px;
}

.card {
  border-radius: 18px;
  border: 3px solid #111;
  padding: 18px;
  min-height: 310px;
  color: #fff;
  display: grid;
  align-content: end;
  gap: 8px;
  background-repeat: no-repeat;
}

.card h3 {
  margin: 0;
  font-size: 20px;
}

.card p {
  margin: 0;
  max-width: 55ch;
  opacity: 0.9;
  line-height: 1.5;
}

.badge {
  display: inline-block;
  width: max-content;
  padding: 6px 10px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(0, 0, 0, 0.35);
  font-size: 13px;
}
  
Background gradients

Radial gradients are images

Layer them, mix them with other backgrounds, and get fancy without extra elements.

Radial gradient color stops and hard edges

Color stops don’t only control “where the color ends”. They also control how sharp the transition is.

  • Soft blends: #00c2ff 0%, transparent 60%
  • Hard edges: #ffd166 0 35%, transparent 35% 100%

That “double stop” trick (same position repeated) is the secret sauce for crisp rings, circles, and cutout-like effects.

CSS radial gradient position

Position controls where the gradient starts (its center). You set it with at:

radial-gradient(at 20% 30%, ...)

You can use keywords (left, center, right, top, bottom) or actual values (px, %).

.stage {
  background-image: radial-gradient(at center, #00c2ff 0%, #111 60%);
}
  
.stage {
  background-image: radial-gradient(at left top, #00c2ff 0%, #111 62%);
}
  
.stage {
  background-image: radial-gradient(at right bottom, #ff3d71 0%, #111 62%);
}
  
.stage {
  background-image: radial-gradient(at 25% 35%, #00ffa3 0%, #111 62%);
}
  
.stage {
  background-image:
    radial-gradient(at 20% 30%, rgba(0, 194, 255, 0.95) 0%, transparent 55%),
    radial-gradient(at 80% 70%, rgba(255, 61, 113, 0.9) 0%, transparent 55%),
    linear-gradient(135deg, #111, #2a2a2a);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  font-family: system-ui, Arial, sans-serif;
  padding: 18px;
  max-width: 980px;
  display: grid;
  gap: 14px;
}

.stage {
  height: 310px;
  border-radius: 18px;
  border: 3px solid #111;
  background-repeat: no-repeat;
  display: grid;
  align-content: end;
  padding: 16px;
  color: #fff;
}

.tip {
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid rgba(255, 255, 255, 0.22);
  padding: 10px 12px;
  border-radius: 14px;
  max-width: 62ch;
}

.tip p {
  margin: 0;
  line-height: 1.5;
}
  

Use at x y to move the gradient’s center. Percentages are relative to the box size.

Position gotcha: percentages are not “from the gradient”

When you write at 25% 35%, those percentages are measured from the element’s box. So if your element changes size (responsive layouts), the gradient center moves in a responsive way too. That’s a feature, not a bug.

CSS radial gradient size

Size controls how far the gradient reaches before it hits the final stop. In radial gradients, size can be:

  • Keywords: closest-side, farthest-side, closest-corner, farthest-corner
  • Explicit: 120px or 120px 80px (two values make an ellipse)

The syntax looks like this (shape and size come before at):

radial-gradient(circle farthest-corner at 30% 30%, ...)

.panel {
  background-image: radial-gradient(closest-side at 30% 30%, #00c2ff 0%, #111 70%);
}
  
.panel {
  background-image: radial-gradient(farthest-side at 30% 30%, #00c2ff 0%, #111 70%);
}
  
.panel {
  background-image: radial-gradient(closest-corner at 30% 30%, #ff3d71 0%, #111 70%);
}
  
.panel {
  background-image: radial-gradient(farthest-corner at 30% 30%, #ff3d71 0%, #111 70%);
}
  
.panel {
  background-image: radial-gradient(140px at 30% 30%, #00ffa3 0%, #111 75%);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  font-family: system-ui, Arial, sans-serif;
  padding: 18px;
  max-width: 980px;
  display: grid;
  gap: 14px;
}

.panel {
  height: 310px;
  border-radius: 18px;
  border: 3px solid #111;
  background-repeat: no-repeat;
  display: grid;
  gap: 8px;
  align-content: start;
  padding: 14px;
  color: #fff;
}

.panel h4 {
  margin: 0;
  font-size: 16px;
}

.panel p {
  margin: 0;
  opacity: 0.9;
  line-height: 1.5;
  max-width: 62ch;
}

.kbd {
  display: inline-block;
  font-size: 12px;
  padding: 2px 6px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(0, 0, 0, 0.35);
}
  

Size keywords

Try closest-side vs farthest-corner. They decide how big the gradient is, based on the element’s geometry.

Explicit sizes are the most direct way

If you want full control, use a length:

  • radial-gradient(140px at 50% 50%, ...) makes a gradient with a 140px radius (for a circle gradient).
  • radial-gradient(200px 120px at 50% 50%, ...) makes an ellipse with two radii.

CSS radial gradient circle

By default, a radial gradient is an ellipse that fits the box. If your element isn’t a perfect square, the gradient will look stretched. If you want a true circle, specify circle.

.hero {
  background-image: radial-gradient(#00c2ff 0%, #111 70%);
}
  
.hero {
  background-image: radial-gradient(circle, #00c2ff 0%, #111 70%);
}
  
.hero {
  background-image: radial-gradient(circle at 25% 35%, #00ffa3 0%, #111 70%);
}
  
.hero {
  background-image: radial-gradient(circle 140px at 25% 35%, #00ffa3 0%, #111 75%);
}
  
.hero {
  background-image:
    radial-gradient(circle 140px at 25% 35%, rgba(0, 255, 163, 0.95) 0%, transparent 70%),
    radial-gradient(circle 160px at 75% 65%, rgba(255, 61, 113, 0.85) 0%, transparent 70%),
    linear-gradient(135deg, #111, #2a2a2a);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  font-family: system-ui, Arial, sans-serif;
  padding: 18px;
  max-width: 980px;
  display: grid;
  gap: 14px;
}

.hero {
  height: 310px;
  border-radius: 18px;
  border: 3px solid #111;
  background-repeat: no-repeat;
  padding: 18px;
  display: grid;
  align-content: end;
  gap: 10px;
  color: #fff;
}

.hero h3 {
  margin: 0;
  font-size: 22px;
}

.hero p {
  margin: 0;
  line-height: 1.5;
  opacity: 0.9;
  max-width: 62ch;
}

.pill {
  width: max-content;
  padding: 6px 10px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(0, 0, 0, 0.35);
  font-size: 13px;
}
  
Circle gradients

Make it a real circle

If your element is wide, the default ellipse looks stretched. Add circle to force a circular gradient.

CSS radial gradient ellipse and oval

Ellipses (aka “ovals”) are the default behavior. That’s not a problem — it’s actually super useful when you want a spotlight that fills a wide banner.

You can control ellipse behavior in two main ways:

  • Let it be automatic: radial-gradient(ellipse at center, ...)
  • Set explicit ellipse radii: radial-gradient(260px 140px at center, ...)
.banner {
  background-image: radial-gradient(ellipse, #ffd166 0%, #111 70%);
}
  
.banner {
  background-image: radial-gradient(ellipse at 30% 40%, #ffd166 0%, #111 70%);
}
  
.banner {
  background-image: radial-gradient(280px 140px at 50% 50%, #00c2ff 0%, #111 75%);
}
  
.banner {
  background-image: radial-gradient(380px 120px at 50% 60%, #00ffa3 0%, #111 80%);
}
  
.banner {
  background-image:
    radial-gradient(420px 150px at 50% 40%, rgba(0, 194, 255, 0.9) 0%, transparent 65%),
    linear-gradient(135deg, #111, #2a2a2a);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  font-family: system-ui, Arial, sans-serif;
  padding: 18px;
  max-width: 980px;
  display: grid;
  gap: 14px;
}

.banner {
  height: 310px;
  border-radius: 18px;
  border: 3px solid #111;
  background-repeat: no-repeat;
  padding: 18px;
  display: grid;
  align-content: center;
  gap: 10px;
  color: #fff;
}

.banner h3 {
  margin: 0;
  font-size: 22px;
}

.banner p {
  margin: 0;
  line-height: 1.5;
  opacity: 0.9;
  max-width: 62ch;
}

.tag {
  width: max-content;
  padding: 6px 10px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(0, 0, 0, 0.35);
  font-size: 13px;
}
  

CSS radial gradient transparent with masks

Here’s a subtle but important truth: gradients don’t create “holes” in an element by default — they only paint colors. If you want a transparent cutout (like a vignette, spotlight reveal, or hole-punch effect), use masking.

That’s why this section uses mask-image and -webkit-mask-image. The mask controls which pixels are visible:

  • White (or opaque) means “show this pixel”.
  • Transparent means “hide this pixel”.

So a radial gradient mask is perfect for fade-out edges, circular reveals, and “transparent” radial gradient effects.

.cutout {
  -webkit-mask-image: radial-gradient(circle, #000 0%, transparent 70%);
  mask-image: radial-gradient(circle, #000 0%, transparent 70%);
}
  
.cutout {
  -webkit-mask-image: radial-gradient(circle at 30% 40%, #000 0%, transparent 70%);
  mask-image: radial-gradient(circle at 30% 40%, #000 0%, transparent 70%);
}
  
.cutout {
  -webkit-mask-image: radial-gradient(circle 120px at 50% 50%, #000 0%, transparent 75%);
  mask-image: radial-gradient(circle 120px at 50% 50%, #000 0%, transparent 75%);
}
  
.cutout {
  -webkit-mask-image:
    radial-gradient(circle 140px at 35% 45%, #000 0%, transparent 78%),
    radial-gradient(circle 120px at 70% 55%, #000 0%, transparent 78%);
  mask-image:
    radial-gradient(circle 140px at 35% 45%, #000 0%, transparent 78%),
    radial-gradient(circle 120px at 70% 55%, #000 0%, transparent 78%);
}
  
.cutout {
  -webkit-mask-image: radial-gradient(280px 160px at 50% 50%, #000 0%, transparent 72%);
  mask-image: radial-gradient(280px 160px at 50% 50%, #000 0%, transparent 72%);
}
  
.cutout {
  -webkit-mask-image: radial-gradient(circle, transparent 30%, #000 70%);
  mask-image: radial-gradient(circle, transparent 30%, #000 70%);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  font-family: system-ui, Arial, sans-serif;
  padding: 18px;
  max-width: 980px;
  display: grid;
  gap: 14px;
}

.frame {
  border-radius: 18px;
  border: 3px solid #111;
  overflow: hidden;
}

.cutout {
  min-height: 380px;
  padding: 18px;
  color: #fff;
  display: grid;
  align-content: end;
  gap: 10px;

  background-image:
    linear-gradient(135deg, #111, #2a2a2a),
    url("https://picsum.photos/1100/700");
  background-size: cover;
  background-position: center;
  background-blend-mode: overlay;

  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;

  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
}

.cutout h3 {
  margin: 0;
  font-size: 22px;
}

.cutout p {
  margin: 0;
  line-height: 1.5;
  max-width: 62ch;
  opacity: 0.92;
}

.note {
  border: 2px dashed #111;
  border-radius: 14px;
  padding: 12px;
  background: #fff;
}

.note p {
  margin: 0;
  line-height: 1.5;
}
  

Radial gradient mask

The mask controls transparency: solid center, fading edges. This is the clean way to do “transparent radial gradients”.

Use both -webkit-mask-image and mask-image for best browser coverage. If a mask isn’t supported, the element will simply appear unmasked.

Masking tip: invert the effect with color stops

If you want the opposite (hide the center, show the edges), swap the stops:

radial-gradient(circle, transparent 30%, #000 70%)

That creates a “hole” in the middle instead of a spotlight reveal.

Learn more about CSS masking in the CSS Mask Interactive Tutorial.

CSS radial gradient animation

Radial gradients can absolutely be animated, but instead of swapping the entire background-image (which can feel jumpy and can be heavier), a smoother beginner-friendly approach is to keep the gradient the same and animate the “image controls” around it:

  • background-position to move the gradient(s) around
  • background-size to make the gradient(s) feel like they’re growing and shrinking

This works especially well when you use layered backgrounds, because each gradient layer can have its own position and size. Let’s build a few animations that stay smooth and predictable.

.orb {
  background-image: radial-gradient(circle, rgba(0, 194, 255, 0.95) 0%, transparent 70%);
  background-repeat: no-repeat;
  background-position: 20% 30%;
  background-size: 260px 260px;
  animation: orb-move 3s ease-in-out infinite alternate;
}

@keyframes orb-move {
to {
background-position: 80% 70%;
}
} 
.orb {
  background-image: radial-gradient(circle, rgba(255, 61, 113, 0.9) 0%, transparent 70%);
  background-repeat: no-repeat;
  background-position: 50% 50%;
  background-size: 220px 220px;
  animation: orb-pulse 1.8s ease-in-out infinite;
}

@keyframes orb-pulse {
  50% {
    background-size: 360px 360px;
  }
}
  
.orb {
  background-image:
    radial-gradient(circle, rgba(0, 255, 163, 0.95) 0%, transparent 70%),
    linear-gradient(135deg, #111, #2a2a2a);
  background-repeat: no-repeat, no-repeat;
  background-position: 30% 40%, 0 0;
  background-size: 280px 280px, 100% 100%;
  animation: orb-drift 2.6s ease-in-out infinite alternate;
}

@keyframes orb-drift {
  to {
    background-position: 70% 55%, 0 0;
  }
}
  
.orb {
  background-image:
    radial-gradient(circle, rgba(0, 194, 255, 0.95) 0%, transparent 70%),
    radial-gradient(circle, rgba(255, 61, 113, 0.85) 0%, transparent 70%),
    linear-gradient(135deg, #111, #2a2a2a);
  background-repeat: no-repeat, no-repeat, no-repeat;
  background-position: 20% 30%, 80% 70%, 0 0;
  background-size: 260px 260px, 320px 320px, 100% 100%;
  animation: two-orbs 3.2s ease-in-out infinite alternate;
}

@keyframes two-orbs {
  to {
    background-position: 70% 25%, 35% 75%, 0 0;
    background-size: 320px 320px, 260px 260px, 100% 100%;
  }
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  font-family: system-ui, Arial, sans-serif;
  padding: 18px;
  max-width: 980px;
  display: grid;
  gap: 14px;
}

.orb {
  height: 420px;
  border-radius: 18px;
  border: 3px solid #111;
  padding: 18px;
  color: #fff;
  display: grid;
  align-content: end;
  gap: 10px;
  background-color: #111;
}

.orb h3 {
  margin: 0;
  font-size: 22px;
}

.orb p {
  margin: 0;
  line-height: 1.5;
  opacity: 0.92;
  max-width: 62ch;
}

.pill {
  width: max-content;
  padding: 6px 10px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(0, 0, 0, 0.35);
  font-size: 13px;
}
  
Animated gradients

Smooth motion with position and size

Instead of swapping background-image, we keep the gradient layers the same and animate background-position and background-size for a smoother feel.

Animation tip: animate position and size for smooth results

Animating background-position and background-size is often smoother than swapping gradients in keyframes, because the browser can interpolate numeric values cleanly. A practical trick is to build your effect from one or more radial-gradient() layers with background-repeat: no-repeat, then animate where those layers sit and how large they are.

Learn more about CSS animations in the CSS Animation Interactive Tutorial.

Common patterns you’ll use a lot

Soft spotlight background

Use a circle or ellipse gradient with a transparent outer color, layered over a base background:

  • radial-gradient(circle at 30% 40%, rgba(...) 0%, transparent 65%)
  • Layer over linear-gradient(...) or an image

Crisp ring or badge

Use hard stops (double stop) to make crisp edges:

  • radial-gradient(#ffd166 0 35%, transparent 35% 100%)
  • repeating-radial-gradient(...) for repeated rings

Cutouts and reveals with masks

When you need “true transparency” (revealing what’s behind the element), reach for:

  • -webkit-mask-image
  • mask-image

Quick troubleshooting

  • “My gradient looks stretched.” Fix: add circle or use explicit sizes like 200px 120px.
  • “I tried transparent but it’s just painting transparency.” Fix: use mask-image to actually cut out pixels.
  • “My layered gradients repeat.” Fix: set background-repeat: no-repeat; (often best as default CSS).
  • “My stop looks blurry, I want it sharp.” Fix: use a double stop: color 0 40%, other 40% 100%.

Wrap up

You now have the full radial-gradient toolbelt:

  • Background basics with multiple layers
  • Position using at x y
  • Size with keywords and explicit radii
  • Circle vs ellipse / oval
  • Transparent cutouts using mask-image and -webkit-mask-image
  • Animation by animating background-position and background-size

Next time you see a fancy “glow blob” in a hero section, you can confidently say, “Yep. That’s just a radial gradient wearing a nice suit.”

Learn more about gradients in general in the CSS Gradient Interactive Tutorial.