ChromaWell

Blog

CSS Gradients: A Practical Guide and Current Trends

The technical mechanics of linear, radial, and conic gradients, why the default interpolation is the most common source of muddy gradients, and where the aesthetic is actually heading in 2026.

Gradients are one of the easiest CSS effects to write and one of the easiest to make look accidentally muddy. The syntax is simple; the part that actually determines whether a gradient looks vivid or gray in the middle is the interpolation space, which most people never touch because the default is invisible until you know to look for it.

The three gradient functions, briefly

.linear { background: linear-gradient(135deg, #ff6347, #4169e1); }
.radial { background: radial-gradient(circle at center, #ffd700, #8b4513); }
.conic  { background: conic-gradient(from 0deg, #ff6347, #ffd700, #4169e1, #ff6347); }

Linear gradients transition along a straight axis (an angle, or a keyword like to right); radial gradients transition outward from a center point; conic gradients sweep around a center point like a color wheel — genuinely useful for building an actual hue-wheel picker UI, a pie-chart-style visualization, or a spinner, in a way linear and radial gradients can't replicate. Build any of these visually with the gradient generator and export the exact resulting CSS with gradient-to-code rather than hand-tuning angle and stop values by trial and error.

The muddy-middle problem, and why it happens

The single most common gradient defect: two vivid, saturated endpoint colors on opposite sides of the color wheel, connected by a gradient that turns visibly gray or brown in the middle. This isn't a rendering bug — it's the mathematically correct result of the default interpolation space. linear-gradient(#ff0000, #0000ff) blends red and blue by interpolating each RGB channel independently and linearly: at the 50% stop, R and B are both roughly halfway, and G stays near zero the whole way through. The straight-line path between red and blue in the RGB cube happens to pass close to gray, not through a vivid purple, because RGB space doesn't track perceptual hue at all — it's just three independent channel ramps blended in parallel.

Modern CSS lets you fix this directly by specifying the interpolation space:

.vivid-gradient {
  background: linear-gradient(in oklch, #e63946, #1d3557);
}

Interpolating in oklch (or in hsl, specifying the shorter hue arc) keeps chroma high through the transition because OKLCH's lightness, chroma, and hue are independent channels rather than a linear blend of R, G, and B — the gradient sweeps through the actual hue wheel rather than collapsing toward its RGB-cube center. The underlying math is covered in full in what OKLCH is and why it matters, and the broader CSS color function landscape this fits into is in modern CSS color functions.

Multi-stop gradients and explicit color stops

Beyond a simple two-color blend, explicit stop positions give fine control over where each color dominates:

.sunset {
  background: linear-gradient(
    to bottom,
    #ff7e5f 0%,
    #feb47b 35%,
    #86a8e7 70%,
    #7f7fd5 100%
  );
}

Uneven spacing between stops (35% and 70% here rather than evenly-spaced thirds) lets you weight a gradient toward a dominant color while still giving the eye a full transition — useful for anything trying to evoke a specific real-world lighting condition, like a sunset or dawn sky. See the curated Sunset Gradient palette for hand-picked stop colors already tuned for exactly this kind of transition.

Where the aesthetic is actually heading

Mesh gradients — multi-point, multi-directional color blends that look more like an irregular painted wash than a single-axis transition — have moved from illustration tools into CSS via layered radial gradients and, more recently, native support in some rendering engines. The effect avoids the very linear, very "2015 app icon" look of a single-angle two-color gradient, at the cost of being harder to hand-author without a visual tool.

Grain and noise overlays on top of otherwise smooth gradients have become common, partly as a deliberate rejection of the ultra-clean, banding-prone flat gradient look, and partly for a practical reason: subtle noise reduces visible color banding on smooth gradients, a real rendering artifact caused by limited color-channel precision on some displays, especially on large, low-contrast gradient fields.

Duotone treatments — mapping a photograph's luminance range onto a two-color gradient instead of showing it in full color — have had a resurgence, especially paired with high-contrast, high-saturation color pairs rather than the more muted duotones common a few years earlier. This is a place where the interpolation-space lesson matters directly: a duotone gradient interpolated in OKLCH between two very different hues holds up better across the midtones than the same duotone in RGB.

Warm-to-cool "aurora" gradients — three or more stops sweeping across an unusually wide hue range (magenta through blue through teal, for instance) — lean specifically on OKLCH or HSL-with-explicit-hue-arc interpolation, because an RGB-interpolated version of the same wide hue sweep desaturates badly across such a large hue distance.

A practical checklist before shipping a gradient

  • Check whether the gradient crosses a wide hue distance (roughly more than 90–120°) between adjacent stops — if so, specify in oklch or in hsl explicitly rather than trusting the RGB default.
  • Add an explicit shorter hue/longer hue direction if using HSL or OKLCH interpolation and the gradient's direction around the wheel matters (the default is shorter, but conic gradients in particular can look wrong if you actually wanted the longer path).
  • Test the gradient with actual text placed over it, not just as a background swatch — gradients frequently create a contrast dead zone somewhere along their length where overlaid text briefly fails WCAG contrast even if it passes at both endpoints; see WCAG contrast explained for the underlying threshold math.
  • If the gradient is meant to evoke a specific real-world reference (sunset, ocean, forest), compare it against a curated palette built around that same intent — Sunset Gradient, Ocean Breeze, and Forest Canopy are three starting points — rather than picking endpoint hues that merely sound right.

A worked conic-gradient example: building a hue wheel from scratch

Conic gradients are the one gradient type genuinely built for showing an actual color wheel, because they sweep hue around a center point the same way a real HSL or OKLCH hue axis does. A minimal working hue-wheel swatch:

\\\css .hue-wheel { width: 240px; height: 240px; border-radius: 50%; background: conic-gradient( in oklch, oklch(65% 0.2 0), oklch(65% 0.2 60), oklch(65% 0.2 120), oklch(65% 0.2 180), oklch(65% 0.2 240), oklch(65% 0.2 300), oklch(65% 0.2 360) ); } \\\

Notice the seven stops share identical lightness (65%) and chroma (0.2) and differ only in the hue angle, stepping in even 60° increments from 0° to a full 360° so the wheel closes seamlessly on itself. Specifying \in oklch\ here matters more than it might for a two-stop linear gradient, because a conic sweep touches every hue on the wheel at least once — an RGB-interpolated version of the identical stop list would visibly dim and desaturate through several stretches of the sweep where adjacent stops sit far apart in RGB-cube distance, even though every individual stop color is fully saturated on its own.

Banding, dithering, and why noise overlays are a real fix, not just a style choice

Color banding — visible discrete steps instead of a smooth gradient — happens because a display or a compressed image format only has a limited number of distinct values per channel (8 bits per channel gives 256 steps in standard sRGB output), and a gradient spanning a large area with a small total color change per pixel can exhaust its available steps, producing visible stair-stepping rather than a smooth ramp. This is most visible on large, low-contrast gradient fields — a subtle light-gray-to-white background gradient across a full-width hero section is a classic case. Adding a low-opacity noise or grain texture on top breaks up the flat color steps by introducing small per-pixel variation, which is why grain overlays are genuinely functional rather than purely a stylistic trend — they're a lightweight, CSS-or-image-based form of dithering, the same general technique older graphics formats used to fake more colors than they actually had available.

Gradient text and the contrast problem nobody checks

A specific accessibility gap worth calling out on its own: gradient-filled text (using \background-clip: text\ with a gradient background and transparent text fill) is visually striking but nearly impossible to contrast-check with a single number, because the text color itself changes continuously across the string. A design that looks fine where the gradient happens to sit at its lighter end can become illegible where the same text crosses into the gradient's darker or more saturated stretch, especially over a busy or colored page background behind it. If gradient text is used for anything beyond a short decorative heading, check contrast at multiple points along the gradient's actual length — not just at one sampled stop — using the contrast checker, and avoid gradient-filled text entirely for body copy or any text carrying essential information.

Performance: gradients are cheap, but not free at scale

A single CSS gradient is computed by the GPU and is essentially free compared to a background image asset — no network request, no decode cost. Where performance actually degrades is stacking many overlapping gradients (a common mesh-gradient technique layers several radial gradients on top of each other) or repainting a large animated gradient every frame, which can measurably affect scroll and paint performance on lower-powered devices. If a mesh-gradient effect needs to animate, prefer animating a cheap property like \background-position\ or an SVG filter over recomputing multiple full-size layered gradients on every frame, and test the actual effect on a mid-range mobile device rather than only a development machine before shipping it broadly.

Get new tools & color guides by email

Occasional emails when a new tool, dataset, or in-depth guide ships. No spam, unsubscribe anytime.