Hover✨ Premium

Gradient Shift

A three-color animated gradient loops slowly at rest (8 s) and speeds up to 2 s on hover — the speed quadruples with a single CSS animation-duration change. No JavaScript.

CSSGradientAnimation

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Hover has 48 effects, including 5 free. Explore the category →

3 usage examples

Music genre buttons — streaming interface — Gradient Shift example 1

① Music genre buttons — streaming interface

WhenSix genre buttons (Hip-Hop, Electronic, Jazz, Indie, Classical, R&B) in a 3×2 grid; at rest each button's background cycles slowly; on hover the animation speeds up to preview the energy of the genre.
WhyThe accelerating animated background metaphorically conveys the tempo and intensity of each music genre, without an extra image or icon.
SettingsButtons 100%×52 px, 3-colour gradient specific to each genre, 8 s at rest, 2 s on hover, scale(1.05).
Premium plan card — SaaS pricing page — Gradient Shift example 2

② Premium plan card — SaaS pricing page

WhenThree plans side by side (Starter, Pro, Scale); the Pro and Scale plans have an animated background that cycles slowly at rest and speeds up on hover to stand out from the free plan.
WhyA paid plan whose background accelerates on hover stands out naturally from the static free plan, without a 'Popular' sticker or highlighted border.
SettingsCards 100%×variable height, gradient specific to each paid plan, free plan with overridden static background, badge + name + price + feature list.
'Last seats' urgency banner — festival site — Gradient Shift example 3

③ 'Last seats' urgency banner — festival site

WhenFull-width banner at the top of a festival page; at rest the background cycles slowly to signal something is there; on hover it speeds up to reinforce urgency.
WhyThe visual acceleration conveys time pressure (limited seats) — a strong semantic signal without extra alert text.
SettingsBanner 100%×60 px, indigo-pink-amber gradient, 8 s at rest, 2 s on hover, text + 'Buy' button in z-index 1 on top.

How it works

The .fx-0360-root root centers a .hover-box.hover-gradient-shift block of 200 × 200 px. The element has a linear-gradient(135deg, #6366f1 0, #ec4899 50%, #f59e0b 100%) 0 0/200% 200% background — a gradient at 200% width and height, allowing background-position to scroll the colors.

Two @keyframes that are geometrically identical (animating background-position from 0 to 100% and back) but with different names (fx0360-gradientIdle and fx0360-gradientFast) allow switching between two durations: 8 s at rest, 2 s on hover. Changing animation-name on hover restarts the animation from the beginning — having two distinct names guarantees a true restart rather than a simple speed-up of the current animation.

The fx0360- prefix prevents name collisions. Measured in Chromium 151: slow animation at rest, visible speedup on hover, two independent copies.

Accessibility

  • Reduced motion: under prefers-reduced-motion: reduce, all animations stop and the scale is cancelled; on hover the element shows a fixed filter: brightness(1.15). The idle animation also stops.
  • Keyboard: the element is a natively focusable <button>. The :focus-visible rule triggers the same gradient speed-up; an indigo outline marks the focus state.
  • Idle animation loop: the idle animation (8 s) runs without interaction. Under prefers-reduced-motion: reduce, it stops. On a content-heavy animated page, consider starting the idle animation only when the element is visible (IntersectionObserver).
  • Touch: the first touch triggers the speedup; it stays active until the next touch.

Browser compatibility

Pure CSS (@keyframes, background-position, animation, background-size, :focus-visible, prefers-reduced-motion), no JavaScript. Measured in Chromium 151: animation visible, speedup on hover, two independent copies. Works in all modern browsers.

Chrome 60+✓ Full (measured)
Firefox 55+✓ Full
Safari 10.1+✓ Full
Edge 79+✓ Full (Chromium engine)
Mobile iOS 10.3+✓ Full
Android Chrome 60+✓ Full (Chromium engine)

Without animation, the background stays static at rest and on hover. Without background-size: 200% 200%, the gradient cannot scroll.

The code

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

index.html — structure
<div class="fx-0360-root">
  <button class="hover-box hover-gradient-shift" type="button">
    <span>…</span>
  </button>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Base gradient (CSS — background of .hover-gradient-shift) Indigo → pink → amber at 200% × 200% Change the three colors to your palette. Keep 0 0/200% 200% so that the position animation scrolls the gradient.
Idle duration (CSS — animation: 8s ease infinite fx0360-gradientIdle) 8 s per cycle 12s for a very slow, discreet idle; 4s for a more visible idle animation.
Hover duration (CSS — animation: 2s ease infinite fx0360-gradientFast) 2 s per cycle (4× faster) 1s for extreme acceleration; 4s for a gentler speedup. The idle/fast ratio defines the intensity of the signal.
Scale on hover (CSS — transform: scale(1.05)) 5% enlargement Switch to scale(1) to disable the enlargement if the background animation alone is sufficient.
@keyframes names (CSS — fx0360-gradientIdle / fx0360-gradientFast) Prefixed fx0360- Only rename if you copy the code into an environment where the prefix conflicts. The two names must stay different to guarantee the restart.

FAQ

Changing animation-name on hover forces the browser to restart the animation from position 0. With a single @keyframes, only changing animation-duration would speed it up without restarting: the current position is kept, which can cause a visual jump.
Yes: remove the animation property from the .hover-gradient-shift selector and keep it only on :hover and :focus-visible. The background will be static at rest and animate only on hover.
Compute the duration in JavaScript: const remaining = endTime - Date.now(); el.style.animationDuration = (remaining/1000).toFixed(1)+'s'. The decreasing duration accelerates the animation as the deadline approaches.