Sunset Mesh
Two blurred radial-gradient layers animate in pure CSS — orange, pink, red and purple loop endlessly without a line of JavaScript.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Backgrounds has 50 effects, including 6 free. Explore the category →
3 usage examples



How it works
The effect uses the CSS mesh gradient technique: several radial-gradient(at X% Y%, color 0, transparent 50%) functions are stacked on the background property of a single element. Each call creates a color blob centered at precise coordinates — three blobs per layer, six in total — whose transparent zones blend naturally into a continuous chromatic mesh.
Both layers have position: absolute; inset: -30%: they overflow the parent container by 30% on every side, making them 160% of its width and height. During scale and translate animations, no dark border can appear because the color blobs always extend beyond the visible frame (overflow: hidden on .sunset-mesh clips the overflow).
A filter: blur(25px) on the first layer and blur(30px) on the second merges the hard edges of the radial gradients into smooth, continuous transitions. The higher the blur value, the silkier the blend; dropping to 8-12 px reveals distinct color zones.
Two asynchronous CSS animations produce organic motion: sunsetPulse (scale 1 → 1.1 → 0.95 → 1.05 → 1 with offset translates, 10 s on layer 1) and sunsetDrift (translate + 5° rotation, 14 s with animation-direction: alternate) on layer 2. Because the durations are not integer multiples of each other and layer 2 oscillates back-and-forth, the composition never repeats exactly.
Accessibility
- prefers-reduced-motion not implemented: no
@media (prefers-reduced-motion: reduce)stops the animations. Add it in your integration:@media (prefers-reduced-motion: reduce) { .sunset-mesh-layer { animation: none !important; } }— the layers remain visible as a static gradient. - The base HTML has no
aria-hidden,role="img"oraria-label— addrole="img" aria-label="Animated sunset gradient background"on.sunset-meshif the element carries semantic meaning. - The
.sunset-mesh-labeltext (rgba(255,255,255,.9)) against the dark base (#0a0a0f) has a contrast ratio ≥ 7:1 (WCAG AAA); warm gradient zones can reduce it locally — verify with your own color palette. - No interactive elements in the effect: keyboard navigation and focus management are not applicable.
- Animations are slow transitions (10-14 s) with no abrupt changes or flicker: compliant with WCAG 2.3.1 (flash threshold < 3 flashes/s).
Browser compatibility
Requires CSS radial-gradient(), filter: blur(), @keyframes and the inset shorthand — supported from Chrome 87, Firefox 87 and Safari 14.1.
On browsers without <code>filter: blur()</code> support (IE 11), the layers appear as static radial gradients without blending — visually degraded but error-free. The <code>inset</code> shorthand can be replaced with <code>top: -30%; right: -30%; bottom: -30%; left: -30%</code> for Edge versions earlier than 87.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="sunset-mesh">
<div class="sunset-mesh-layer"></div>
<div class="sunset-mesh-layer"></div>
<!-- Optional: text or overlay content -->
<span class="sunset-mesh-label">SUNSET</span>
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| at X% Y% positions (layer 1) | 30% 20% · 70% 30% · 50% 80% | Coordinates of the three color blobs in .sunset-mesh-layer:nth-child(1) — move them to reposition the orange, pink and purple masses. |
| RGBA layer 1 | rgba(255,120,0,.7) · rgba(255,60,100,.6) · rgba(180,0,180,.5) | Colors of the orange, pink-red and purple blobs on layer 1 — swap in your brand palette (e.g. rgba(0,120,255,.7) for a cool mood). |
| RGBA layer 2 | rgba(255,50,50,.5) · rgba(255,180,0,.4) · rgba(200,0,150,.45) | Red, gold and magenta tones on layer 2 — keep alpha ≤ 0.5 to stay below layer 1 intensity. |
| filter: blur() — layer 1 | blur(25px) | Gaussian blur on .sunset-mesh-layer:nth-child(1) — increase (40px+) for a very soft blend; decrease (8-12px) for distinct color zones. |
| filter: blur() — layer 2 | blur(30px) | Gaussian blur on .sunset-mesh-layer:nth-child(2), slightly higher by default — tune alongside layer 1 for the desired render. |
| animation-duration sunsetPulse | 10s (layer 1) | Scale+translate cycle duration for layer 1. Set to 20-30s for ambient background; decrease to 5s for energy. |
| animation-duration sunsetDrift | 14s | Oscillation cycle for the layer 2 alternate drift. Set to 28s so a static export never lands on an extreme layer position. |
| inset — .sunset-mesh-layer | -30% | Layer overflow outside the container. Increase (e.g. -40%) if dark edges appear at large viewport sizes. |
| .sunset-mesh dimensions | width: 320px; height: 220px | Fixed demo size — switch to width: 100%; height: 100% (or 100vh) to fit any parent container. |
FAQ
width: 320px; height: 220px) with width: 100%; height: 100% on .sunset-mesh and make sure its parent has a defined height (e.g. height: 100vh). The inset: -30% on the layers ensures edges remain invisible regardless of container size.@media (prefers-reduced-motion) block. For an accessible integration, add to your CSS: @media (prefers-reduced-motion: reduce) { .sunset-mesh-layer { animation: none !important; } } — the layers remain visible as a static gradient, preserving the visual effect without any motion.