Design FIELD NOTES · 4 MIN READ
How CSS Gradients Work
A plain-English tour of CSS gradients: linear, radial, and conic functions, angle conventions, color stops and hard edges, alpha stops, and contrast over fades.
A CSS gradient looks like an effect, but it is really a kind of image: the browser computes a picture from a formula — colors along a line, out from a point, or sweeping around one — and paints it as the background. Three functions generate every gradient on the web, and their angles, stops, and color formats follow a few rules worth knowing once.
A gradient is an image, not a color
linear-gradient() and its siblings are functions that produce images, so they belong in background-image (or the background shorthand), not background-color — a common slip that makes the declaration appear to "do nothing". The generated image scales to fill the element's box by default, and like other background layers it stacks: several gradients can be listed comma-separated with the first one painted on top, which is how layered overlays are built.
linear-gradient: colors along a line
A linear gradient travels in one direction across the box. The angle is measured so that 0deg points up, 90deg points right, and larger angles rotate clockwise:
background-image: linear-gradient(90deg, #4f8cff, #22c55e);
/* left edge #4f8cff, sliding to #22c55e at the right edge */
Direction keywords work too — to top right, to bottom — and translate to the nearest equivalent angle. The mental model that survives contact with reality: the angle describes where the last color arrives, and the color bands run perpendicular to that direction. Rotate the angle by 90° and the stripes turn with it.
radial and conic: out from a point, around a point
radial-gradient() spreads outward from a center point, so the color bands are rings — the default shape is an ellipse sized to the box, with circle forcing round rings:
background-image: radial-gradient(circle, #4f8cff, #22c55e);
conic-gradient() sweeps around a point like the hand of a clock, starting at 12 o'clock by default and rotating clockwise — the function behind pie charts, color wheels, and loading rings:
background-image: conic-gradient(from 90deg, #4f8cff, #22c55e);
The two are easy to confuse because both are "centered". If the bands are rings, it is radial; if they are wedges, it is conic.
Stops, positions, and hard edges
Each color in the list is a color stop. By default the browser spreads them evenly from 0% to 100% along the gradient line, interpolating smoothly between neighbors. Explicit positions reshape that curve — and a "double stop" (the same color at two positions) creates an instant transition, the trick behind stripes and segmented rings:
background-image: linear-gradient(
90deg,
#4f8cff 0%,
#4f8cff 50%,
#22c55e 50%,
#22c55e 100%
);
/* left half solid #4f8cff, right half solid #22c55e */
Modern CSS shortens each pair with double positions: #4f8cff 0 50% means "this color holds from 0% through 50%". Anything left of the first stop stays that first color, and anything past the last stop stays the last — gradients do not fade to nothing at the edges unless you say so.
When hex is not enough: rgb() and alpha
Six-digit hex colors cannot express transparency, and gradients constantly need it — overlays that fade to nothing, glassy panels, image scrims. That is where rgb() earns its keep: #4f8cff is rgb(79, 140, 255), and appending an alpha gives rgba(79, 140, 255, 0), a fully transparent version of the exact same color. That precision matters because a fade "to transparent" should fade to transparent blue, not to opaque gray: rgba(79, 140, 255, 0) interpolates cleanly, while a stop of transparent blends through black in most browsers. This is also why 8-digit hex exists — #4f8cff00 — though rgb()/rgba() remains the more readable form when you are doing arithmetic on the channels.
Text on a gradient: check the worst stop
A gradient background is a contrast problem with a moving target. WCAG requires a 4.5:1 ratio for normal text (3:1 for large text) against the background behind each glyph, so a white heading that passes on a dark navy stop can fail where the gradient lightens. The practical rule: check the text color against the lightest stop and the darkest stop it touches — if the text sits in the middle of the fade, both ends must pass. If they cannot, shift the stop positions so the text region of the gradient stays within a safe range, or add a scrim layer. The arithmetic behind those thresholds is the same relative-luminance formula described in how color contrast works.
Try it yourself
The CSS Gradient Generator composes linear, radial, and conic gradients from two colors plus an angle, with a live preview and a copyable background declaration. The Hex to RGB Converter does the channel arithmetic when a stop needs an alpha channel. And the Color Contrast Checker scores any foreground/background pair against the WCAG thresholds — run it on both ends of your gradient before shipping text over it.