CSS Mesh Gradient Technique with layered radial gradients
Layering multiple radial gradients with transparent edges creates the soft, blended look of a mesh gradient using nothing but standard CSS background-image values. Each layer contributes a colored blob at a specific position, and the layers blend where they overlap. No special CSS feature is required: comma-separated background-image values stack radial gradients from front to back.1
The key is transparency: each gradient layer uses rgba() or oklch() colors with alpha values below 1 for the outer stops, letting lower layers show through in the overlap regions.2 The result resembles the smooth multicolor mesh gradients from design tools like Figma, built entirely in CSS without SVG or canvas. Consequently, mesh gradients work in all modern browsers without polyfills. Building on this, adjusting the radial size and position of each layer controls how far each color blob extends and where the blended overlap regions appear on the element.
Layering multiple radial-gradient() values
CSS accepts comma-separated background-image values. Each value renders as a separate layer, with the first value on top and subsequent values below. For a mesh effect, each layer is a radial-gradient() positioned at a different point with a color that fades to transparent at its edges. The number of layers determines the complexity of the mesh: three layers produce a simple blend, while five or six layers create the rich, organic look associated with design tool mesh gradients.
A three-blob mesh uses three layers: radial-gradient(circle at 30% 40%, oklch(0.75 0.2 250 / 0.8) 0%, transparent 60%), followed by two more layers at different positions with different colors. The browser composites the layers using the normal blend mode by default.1 Consequently, overlapping regions show additive blending through the alpha channel: where a blue blob at 0.8 opacity overlaps a red blob at 0.7 opacity, the result is a purple region. Controlling each blob's alpha at the center stop adjusts how saturated the overlap regions appear. The number of layers determines the complexity of the mesh: three layers produce a simple blend, while five or six layers create the rich, organic look associated with design tool mesh gradients. Positioning the blobs at slightly irregular intervals rather than on a strict grid prevents the pattern from reading as geometric.
Using transparent stops to reveal lower layers
The outer stop of each radial gradient must use a transparent or low-alpha color for the mesh effect to work. A gradient from a solid color to transparent(oklch(0.7 0.2 250)) fades to nothing at its outer edge, revealing the layers below.2 Without transparency in the outer stop, each layer covers all layers beneath it completely and the mesh effect disappears. Choosing a base background color that matches the average hue of the gradient blobs prevents the uncovered regions from appearing as a mismatched background that clashes with the overall mesh composition.
Adjusting blend coverage
The percentage position of the transparent stop controls how far each blob extends before fading. Moving the outer stop from 60% to 80% makes the blob cover more of the element before fading to transparent. Moving it to 40% makes a smaller, more concentrated blob. Pair the stop position with an alpha value below 0.5 at the center to create an even softer blend where the overlap regions between blobs sit at similar perceived brightness, preventing any one blob from appearing to sit perceptually on top of the others in the stack.
Yet very small blobs with an outer stop below 30% may leave large uncolored regions between blobs that break the mesh illusion and reveal the underlying background in unflattering patches. Furthermore, the base background color of the element shows through in regions not covered by any blob, so set a background-color that complements the gradient blobs to control the mesh's overall tone. For dark-mode mesh gradients, reducing the chroma of each blob to 0.05 or below and keeping lightness between 0.12 and 0.25 produces a subtle atmospheric texture that reads as depth rather than as a set of colored circles floating on a dark surface.
Adjusting radial size and position for intersection blends
Each layer's radial size and center position determines where blobs overlap. Positioning two blobs within 20% to 30% of each other on both axes creates a visible overlap region where the colors blend. Positioning them farther apart produces isolated color pools with minimal blending. Using oklch() colors with consistent lightness values across all blobs produces a mesh where no single color dominates perceptually, keeping the overall composition balanced.
Building a mesh grid
A systematic approach places blobs at grid positions: 25% 25%, 75% 25%, 25% 75%, and 75% 75% for a four-blob mesh. Each blob uses a different hue and covers a radius large enough to overlap with its neighbors, typically 40% to 60% of the element size. Starting with a four-blob grid lets you validate that the color relationships read as intended before layering in additional complexity, and adjusting the radius of every blob by the same percentage keeps the overlap ratios consistent as the mesh grows or shrinks across breakpoints.
The overlap regions at the grid intersections show blended colors from two or more layers. Adding a fifth blob at the center pulls the mesh together visually and eliminates any uncolored region near the element midpoint. Using oklch() colors with consistent lightness values across all blobs produces a mesh where no single color dominates perceptually. For hero sections that need the mesh to feel organic rather than grid-based, offsetting each blob from its grid position by a random small percentage breaks the rigid symmetry and produces a more natural distribution.
Controlling mesh blob positions with CSS custom properties
CSS custom properties make mesh gradient blob parameters configurable without editing the gradient function directly. Declaring custom properties for each blob's x position, y position, and size at the component level lets you adjust each blob through a single variable block. Applying all three parameters as custom properties reduces the multi-layer gradient to a readable template where the circle center and outer radius reference the variables.
Animating blob positions with @property creates a flowing mesh that shifts color pools smoothly over time.3 Registering each position property as type number (representing a percentage) allows @keyframes to interpolate between positions without the discrete snap behavior that untyped custom properties produce. A four-blob mesh with independently animating positions produces a slow, aurora-like surface motion with no JavaScript.
Limiting animation to prefers-reduced-motion off
Wrapping the @property animation in @media (prefers-reduced-motion: no-preference) respects the user's system-level motion preference.4 Users who have enabled reduced motion see the static mesh gradient, while users without that preference see the animated version. This pattern applies to any CSS gradient animation and is a baseline accessibility requirement for production deployments. Combining this media query with the @property registration ensures that only browsers supporting both features run the animation, providing a clean fallback to the static gradient in older browsers and respecting user preferences simultaneously.
When to use this
Use mesh gradients for hero backgrounds, card surfaces, and decorative panels where a single-direction gradient feels too simple. On small elements the blending area is often too tight to read as a mesh rather than a smeared color, so see where your mesh blobs actually overlap at the size you plan to ship before committing to the effect.
Examples
Hero section background with four-color mesh
Place four radial blobs at 25% 25%, 75% 25%, 25% 75%, 75% 75%. Use oklch colors with hues 90 degrees apart for adjacent blobs. Set the outer stop to transparent at 60% for each layer. Add a base background-color matching the darkest gradient tone.
Card surface with a subtle two-color mesh
Use two overlapping blobs, one at 30% 30% and one at 70% 70%. Keep alpha values at 0.3 to 0.5 for a muted effect that does not compete with card content. The blending midpoint between the blobs creates a natural focal point for the card layout.
Dark-mode mesh gradient for a sidebar panel
Use oklch colors with lightness below 0.25 and chroma below 0.1 for dark surface blobs. High-chroma blobs look neon on dark backgrounds rather than atmospheric. Reduce chroma to 0.05 to 0.08 for subtle depth without color noise.
- 1.
W3C, "CSS Backgrounds and Borders Module Level 3," w3.org, July 2024. https://www.w3.org/TR/css-backgrounds-3/#background-image
- 2.
CSS-Tricks, "radial-gradient()," css-tricks.com, July 2025. https://css-tricks.com/almanac/functions/r/radial-gradient/
- 3.
Mozilla Developer Network, "@property," developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Web/CSS/@property
- 4.
Mozilla Developer Network, "prefers-reduced-motion," developer.mozilla.org, June 2026. https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion