Transitions✨ Premium

Gradient Shift

The mood background goes from a blue-violet night gradient to a golden one in 0.8 s, its violet glow turns amber and the text follows: the gold gradient lives in a layer laid over the night and its opacity is what animates, so the switch is continuous in every browser. 0.6 KB of JavaScript, 150 ms when the user reduces motion.

CSSGradientAmbient

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Transitions has 52 effects, including 9 free. Explore the category →

3 usage examples

Night paddle to dawn paddle — a sea kayak rental's booking page — Gradient Shift example 1

① Night paddle to dawn paddle — a sea kayak rental's booking page

WhenThe customer hesitates between the 10 pm bioluminescence outing and the 6:15 am sunrise: at each slot tapped, the whole screen goes from the sea night to a pink-orange dawn.
WhyThe whole atmosphere changes with the slot, like daybreak: the customer feels the outing they pick before reading the time, and the glow becomes a rising sun.
SettingsFull-page scene, 540 × 360 px; gradients replaced (night #020617 → #0c4a6e at 180°, dawn #fcd9a8 → #f4a3a0); glow enlarged to 120 px at the top right; dark SVG waves placed between the background and the text; two slot buttons that set active.
Off-peak to peak hours — an electricity supplier's app — Gradient Shift example 2

② Off-peak to peak hours — an electricity supplier's app

WhenAt 6 am, the app goes from 'off-peak, €0.2068 per kWh' on a midnight-blue background to 'peak, €0.2700' on a golden one; the day's consumption chart stays in place.
WhyThe mood gradient encodes the rate period with no text to read; the glow follows the color, and the label stays for anyone who cannot tell the colors apart.
SettingsThe effect's gradients and glow kept as they are, glow enlarged to 150 px; chart bars with their own 0.8 s color transition; 'Off-peak / Peak' buttons at the top right.
End of the session — a light therapy lamp's app — Gradient Shift example 3

③ End of the session — a light therapy lamp's app

WhenThe session started at 6:30 am reaches its 30 minutes at 10,000 lux: the dark background turns warm gold and the progress ring fills at the same time.
WhyThe final warm color matches the light received: the user, eyes half closed, knows the session is over without looking at the figure.
SettingsGradients replaced (deep brown #0c0a09 → #292524, gold #fff7d6 → #f59e0b); 200 px glow behind the ring; SVG ring whose stroke-dashoffset follows the same 0.8 s duration.

How it works

The scene .th-gradient-scene (300 × 220 px, 16 px corners) holds a .th-gradient-bg background with position: absolute; inset: 0: linear-gradient(135deg, #0f172a, #1e1b4b, #312e81), a night going from slate blue to indigo. On top, its ::after pseudo-element covers the same area with the golden linear-gradient(135deg, #fef3c7, #fde68a, #fcd34d), at opacity 0. In the center, the content layer (z-index: 1) stacks an 80 px glow, the 1.2 rem bold title and a line of text. The glow follows the same principle: a 40% violet radial-gradient below, a 50% amber one in its ::after, invisible at rest.

A gradient does not interpolate from one value to another: declared directly in place of the first under .active, it replaces the old one at once (measured: the final gradient shows 60 ms after the click in Chromium and Firefox, despite a 0.8 s transition). Hence the two layers: .active takes the opacity of both ::afters from 0 to 1 with transition: opacity .8s, while the title goes from #fff to #78350f and the line from 50% white to #92400e (transition: color .8s). Since both gradients share the same geometry and the golden layer is opaque, the blend at each pixel is exactly what you would get if each color slid to its counterpart. Measured 300 ms after the click: golden layer at 0.65 opacity in Chromium 151, 0.61 in Firefox 153, 0.66 in WebKit 26, then 1 at 1.5 s; the second click goes back to night.

The script (0.6 KB) arms every .th-gradient-scene declared role="button": click, Enter and Space toggle .active and aria-pressed. Without that role, the scene is left alone and you set the class from your own control (a time slot, an hour, a timer ending), as in the examples. The two gradients are swapped in CSS without touching anything else: only the two background rules of the backdrop and its ::after decide the mood. Opacity is animated by the compositor, without repainting the gradients every frame. The effect does not read prefers-color-scheme and remembers nothing.

Accessibility

  • Keyboard and role: the demo scene is a focusable role="button" named 'Golden mood', with aria-pressed. Measured: reached with the Tab key in Chromium and WebKit, toggled by Enter and Space in all three engines; a 2 px amber outline on keyboard focus. In production, prefer a visible button that drives the mood.
  • Reduced motion: nothing moves; under prefers-reduced-motion: reduce, the fade and the text colors take 150 ms. Measured: golden layer at 0.67 (Chromium) and 0.35 (Firefox) opacity 60 ms after the click, 1 at 300 ms.
  • Contrast: on the night, line of text at 5.0:1 and white title well above; on gold, #78350f title at 7.3:1 and #92400e line at 5.7:1. During the 0.8 s, text and background go through mid tones together: avoid switching while critical information is being read. Color alone should not carry meaning (off-peak or peak): keep a label.

Browser compatibility

CSS (::after pseudo-element, opacity and color transitions) + 0.6 KB of JavaScript, no dependency, no @property, no prefix. Measured in Chromium 151, Firefox 153 and WebKit 26: the same continuous fade, the same values within a few hundredths, same way back. Opacity transitions on pseudo-elements are much older; the minimum versions come from inset: 0, which spreads the gradient over the whole scene: Chrome and Edge 87, Firefox 66, Safari 14.1 (iOS 14.5).

Chrome 87+✓ Full
Firefox 66+✓ Full
Safari 14.1+✓ Full
Edge 87+✓ Full
Mobile iOS 14.5+✓ Full
Android Chrome 87+✓ Full

Without JavaScript, the scene stays on the night and does not react; setting active in the HTML serves the golden mood directly.

The code

HTML structure to paste into your page (the CSS + JS come with Premium):

index.html — structure
<div class="th-gradient-root">
  <div class="th-gradient-scene" role="button" tabindex="0" aria-pressed="false" aria-label="Golden mood">
    <div class="th-gradient-bg"></div>  <!-- night + golden ::after -->
    <div class="th-gradient-content">
      <div class="th-gradient-orb"></div>  <!-- glow + amber ::after -->
      <h3>…</h3>
      <p>…</p>
    </div>
  </div>
</div>
Unlock the full code — $9.90/mo incl. tax

The full HTML + CSS + JS of Gradient Shift, ready to paste — plus all 840 effects. Or $299 once, lifetime access.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
The two moods (CSS — background of .th-gradient-bg and .th-gradient-bg::after) night #0f172a → #312e81, gold #fef3c7 → #fcd34d, at 135° Any gradients work; keep the same geometry (angle, stop positions) in both for a change that looks like colors sliding rather than an overlay.
Glow (CSS — .th-gradient-orb and its ::after) 80 px, 40% violet then 50% amber Enlarge it (150 to 200 px) for a sun or a moon; remove it for a plain background.
Text colors (CSS — h3 and p under .active) #78350f and #92400e on gold Check 4.5:1 against the area of the gradient closest to the text, not just its average color: for these dark texts, that is the darkest one, #fcd34d, where #92400e drops to 4.92:1 (6.37:1 on #fef3c7).
Duration (CSS — .8s on the fade and colors) 0.8 s A mood can take 1.2 s; under 0.4 s, the change looks like a jump.
Trigger (HTML — role="button" on the scene) the whole scene is the button Remove role, tabindex and aria-pressed and set .active from your control: the script no longer touches the scene.
Reduced motion (CSS — transition-duration: .15s) 150 ms 0s for an instant change.

FAQ

Both give a continuous change. With @property, you declare each stop color as a <color> (--g1, --g2, --g3) and animate those variables; that takes three declarations per gradient, and the gradient jumps in browsers without @property (before Chrome 85, Safari 16.4 and Firefox 128). The fading layer works everywhere, animates only opacity, and, with both gradients sharing the same geometry, gives the same blend at every pixel. On the other hand, it can only switch between two moods per pseudo-element.
Add one element per mood inside .th-gradient-bg, each with position: absolute; inset: 0, its gradient and transition: opacity .8s, and make only the current one opaque (one class per state, for instance .is-noon). The base background stays visible when all layers are at 0.
Yes, your code decides when to set active: remove role="button" from the scene so it no longer reacts to clicks. On load, read the time (var h = new Date().getHours(); scene.classList.toggle('active', h >= 6 && h < 22);) before the first paint, otherwise the fade plays when the page opens; then schedule the next switch with a setTimeout computed up to 6 am or 10 pm: set live, the class plays the 0.8 s fade in front of the user. A tab in the background delays timers: redo the calculation on the visibilitychange event. The effect does not read prefers-color-scheme: it is a mood tied to content, not an interface theme.