CSS Gradients
A CSS gradient is a smooth transition between two or more colors, generated entirely by the browser instead of downloaded as an image. Because gradients are produced by the rendering engine at paint time, they scale perfectly to any size, stay crisp on high-density screens, and can be animated or updated with CSS custom properties without any extra network requests. Gradients are used everywhere on the modern web: hero banners, buttons, overlays on photos, progress rings, and decorative text.
Overview: How Gradients Work
A gradient is not a color and not a real image file — it is a special kind of <image> value in CSS, produced by a function like linear-gradient(), radial-gradient(), or conic-gradient(). Because a gradient’s data type is <image>, it can be used anywhere an image is accepted: background-image (and the background shorthand), border-image, mask-image, and even content on generated boxes. It cannot be assigned to background-color, since that property only accepts a single <color>, not an image.
Every gradient is built from a list of color stops: colors placed at specific positions along a line (linear), from a center point outward (radial), or around a center point (conic). The browser calculates the exact color at every pixel by interpolating between the two nearest color stops. If you only give two stops — say red at 0% and blue at 100% — the engine computes a smooth blend across the entire distance. Add more stops, and you get more transition segments, each interpolated independently.
Gradients participate in layout exactly like any other background image: they are painted inside the element’s background painting area (by default, the border box), respect background-position, background-size, background-repeat, and background-clip, and can be layered — you can stack multiple gradients, or a gradient over a photo, using comma-separated background layers. Because a gradient has no intrinsic size of its own, it stretches to fill the size of the background painting area unless you constrain it with background-size.
The three gradient types
| Function | Shape | Typical use |
|---|---|---|
linear-gradient() |
Colors transition along a straight line at a given angle or direction | Banners, buttons, subtle depth on cards |
radial-gradient() |
Colors radiate outward from a center point in a circle or ellipse | Spotlights, glows, badges |
conic-gradient() |
Colors sweep around a center point like a clock face | Pie charts, color wheels, progress rings |
Each function also has a repeating- variant (repeating-linear-gradient(), repeating-radial-gradient(), repeating-conic-gradient()) that tiles the color-stop pattern infinitely, useful for stripes and rings.
Syntax
selector {
background: linear-gradient(direction-or-angle, color-stop-1, color-stop-2, ...);
}
- direction-or-angle — optional. A keyword like
to right,to bottom left, or an angle like135deg. If omitted, the default direction isto bottom. - color-stop — a color, optionally followed by one or two positions (a percentage or length), e.g.
#ff0000 25%. You can list as many stops as you like, separated by commas. - color hint — an optional bare percentage between two stops, e.g.
red, 30%, blue, which shifts the midpoint of the interpolation without adding a new color.
For radial-gradient(), the first argument instead describes the shape and size (circle, ellipse, or a keyword like closest-side) and an optional position with at, for example radial-gradient(circle at 50% 30%, ...). For conic-gradient(), the first argument is an optional starting angle with from and a center position with at, for example conic-gradient(from 90deg at 50% 50%, ...).
Examples
Example 1: A basic two-color linear gradient
.hero {
background: linear-gradient(to right, #4f46e5, #06b6d4);
min-height: 240px;
border-radius: 12px;
}
Applied to a <div class="hero">. Result: the box is filled with a smooth horizontal blend that starts as indigo (#4f46e5) on the left edge and ends as cyan (#06b6d4) on the right edge, with every intermediate shade rendered in between. No stop positions were given, so the browser spreads the two colors evenly across the full width: the first color starts at 0% and the last color ends at 100% automatically.
Example 2: An angled gradient with multiple stops
.sunset-card {
background: linear-gradient(135deg, #ff7e5f 0%, #feb47b 45%, #ffcc70 100%);
padding: 48px 32px;
border-radius: 16px;
color: #3a1d0a;
}
Result: the color line runs diagonally at 135 degrees (from the top-left corner toward the bottom-right corner). Coral (#ff7e5f) fills the top-left area, blends into a warm orange (#feb47b) by the 45% mark, and finishes as a golden yellow (#ffcc70) in the bottom-right corner, producing a sunset-like effect. Notice the middle stop is placed at 45% rather than the automatic 50% midpoint — this shifts more of the card toward the orange tone before the yellow takes over.
Example 3: A radial gradient spotlight badge
.spotlight-badge {
width: 140px;
height: 140px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #fef08a 0%, #f59e0b 55%, #b45309 100%);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25);
}
Result: a circular badge that looks like a glossy sphere lit from the upper-left. Because the gradient’s center is offset to 30% 30% instead of the default center, a bright yellow highlight (#fef08a) appears near the top-left of the circle, deepening through amber (#f59e0b) and finishing as a dark brown-orange (#b45309) toward the bottom-right edge, giving a convincing sense of 3D lighting on a flat element.
Under the Hood: How the Browser Computes a Gradient
For a linear-gradient(), the rendering engine performs roughly these steps every time it paints the element:
- 1. Determine the gradient line. The angle or keyword direction (like
to right) is converted into a line passing through the center of the background painting area. Keyword directions are actually converted to an equivalent angle internally —to rightis90deg,to bottomis180deg, and so on. - 2. Calculate the line’s length. For angled corner-to-corner gradients like
to bottom right, the engine computes the length using the box’s width and height so the gradient line always spans from one exact corner to the other — this is why corner keywords behave differently on a square element than on a wide rectangle. - 3. Place the color stops. Stops without an explicit position are spread evenly in the remaining gaps between positioned stops. If two stops would land at the same or an earlier position than a previous stop, the engine bumps the later one forward so positions never move backward.
- 4. Interpolate colors between each pair of stops. By default this happens in the sRGB color space along a straight numeric blend of each color channel, stop to stop, which is why a naive red-to-green gradient often passes through a dull, muddy brown in the middle instead of a vivid yellow.
- 5. Paint pixel by pixel. For every pixel in the background painting area, the engine projects that pixel’s position onto the gradient line and looks up the interpolated color at that point along the line.
A conic-gradient() follows the same stop and interpolation logic, but stops are placed as angles swept clockwise around the center point instead of positions along a line, which is exactly what makes it well suited to pie charts and radial progress indicators:
.progress-ring {
width: 160px;
height: 160px;
border-radius: 50%;
background: conic-gradient(#22c55e 0deg 216deg, #e5e7eb 216deg 360deg);
}
Result: a circle that looks like a pie chart, filled with solid green from 0 to 216 degrees (60% of the circle, representing a 60% progress value) and solid gray for the remaining 144 degrees, with a hard edge between them since each color stop repeats the same color at the start and end of its range instead of blending.
Repeating variants use the same math but restart the pattern once the defined stops are exhausted, tiling until the element is filled:
.striped-bg {
background: repeating-linear-gradient(45deg, #e0e7ff 0px, #e0e7ff 10px, #ffffff 10px, #ffffff 20px);
height: 200px;
}
Result: diagonal stripes at a 45 degree angle, alternating a light lavender (#e0e7ff) band and a white band, each 10 pixels wide, repeating seamlessly across the whole element.
Common Mistakes
Mistake 1: Forgetting the comma after a direction keyword
The direction or angle is a separate argument from the color stops and must be followed by a comma. Leaving it out is a genuine syntax error, not just a style issue:
.hero {
background: linear-gradient(to right #4f46e5, #06b6d4);
}
Without the comma after to right, the browser cannot tell where the direction ends and the first color begins, and it discards the entire declaration as invalid — the element gets no background at all. The fix is simple:
.hero {
background: linear-gradient(to right, #4f46e5, #06b6d4);
}
Mistake 2: Expecting a background gradient to show through text
A very common attempt at “gradient text” is to just put a gradient on the background of a text element and expect it to color the letters:
.gradient-text {
background: linear-gradient(to right, #f43f5e, #8b5cf6);
}
This is valid CSS, but the result is not what beginners expect: the gradient paints behind the text as a rectangular background, while the text itself stays in its normal solid color (typically black), completely hiding the gradient underneath the opaque letters. To actually color the text with the gradient, you must clip the background to the shape of the glyphs and make the text color transparent so the gradient shows through:
.gradient-text {
background: linear-gradient(to right, #f43f5e, #8b5cf6);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
With background-clip: text, the browser uses the text glyphs themselves as the clipping mask for the background layer, so only the pixels inside each letter show the gradient, and color: transparent stops the default text color from covering it.
Best Practices
- Use
to right/to bottomstyle keywords when you want a predictable, box-relative direction, and numeric angles like135degwhen you want the exact same visual angle regardless of the element’s aspect ratio. - Add explicit stop positions once you have more than two colors — relying on automatic even spacing gets harder to reason about as stops multiply.
- Use color hints (a bare percentage between two stops) to shift where the midpoint of a blend falls, instead of adding a third color you don’t actually want to see.
- Store gradient colors in CSS custom properties (e.g.
--brand-start,--brand-end) so a theme or dark-mode swap only needs to update the variables, not every gradient declaration. - Prefer
conic-gradient()over multiple absolutely-positioned triangles or a background image when building pie charts, color wheels, or circular progress indicators. - Combine a gradient with a photo by layering it as a semi-transparent overlay (e.g.
linear-gradient(rgba(0,0,0,0.6), transparent), url(...)) to guarantee text stays readable on top of any image. - Test gradients that rely on saturated opposite hues (like red to green) for muddiness in the middle; consider passing through a third, brighter intermediate stop if the blend looks dull.
Practice Exercises
- Exercise 1: Create a
.cardclass with alinear-gradient()background that goes from a deep blue at the top to a lighter sky blue at the bottom, using theto bottomdirection and two color stops. - Exercise 2: Build a circular “sun” badge using
radial-gradient()with at least three color stops (a bright yellow center, an orange middle, and a deep red edge), and position the center of the radial gradient off-center to suggest lighting from one side. - Exercise 3: Use
conic-gradient()to build a simple pie chart representing three categories that take up 50%, 30%, and 20% of the circle respectively, each in a distinct solid color with hard edges between segments (hint: repeat each color at both the start and end angle of its slice).
Summary
- CSS gradients are computed images, not colors, so they can only be used with image-accepting properties like
background-image, notbackground-color. linear-gradient()blends colors along a straight line at a chosen angle or direction;radial-gradient()radiates colors outward from a point;conic-gradient()sweeps colors around a point.- Color stops can have explicit positions; stops without a position are spread evenly, and color hints let you shift the midpoint of a blend without adding a color.
- Repeating variants (
repeating-linear-gradient(), etc.) tile the stop pattern infinitely, which is ideal for stripes and rings. - Gradient text requires
background-clip: textpluscolor: transparent— a plain background gradient alone will not color the letters. - Always include the comma between the direction/angle argument and the first color stop; omitting it is a syntax error that invalidates the whole declaration.
