20 CSS Animated Buttons 07 / 20

CSS Moving Gradient CTA Button

An eye-catching call-to-action button with an infinite colour-cycling gradient background that shifts through violet, cyan, and rose — no JavaScript, no images.

Pure CSS MIT licensed
Live Demo Open in tab
Open in playground

The code

<div class="ab-07">
  <div class="ab-07__card">
    <p class="ab-07__eyebrow">Limited Time Offer</p>
    <h2 class="ab-07__heading">Start Your Free Trial</h2>
    <p class="ab-07__body">14 days free, no credit card required. Cancel anytime with one click.</p>
    <button class="ab-07__btn">Claim Your Free Trial →</button>
  </div>
</div>
.ab-07,.ab-07 *,.ab-07 *::before,.ab-07 *::after{box-sizing:border-box;margin:0;padding:0}
.ab-07 ::selection{background:#7c3aed;color:#fff}
.ab-07{
  font-family:system-ui,sans-serif;
  background:#0a0a14;
  min-height:100vh;
  display:flex;
  align-items:center;
  justify-content:center;
  padding:2rem;
}
.ab-07__card{
  background:#13131f;
  border:1px solid rgba(255,255,255,.07);
  border-radius:16px;
  padding:2.5rem 2rem;
  max-width:440px;
  text-align:center;
}
.ab-07__eyebrow{
  font-size:.72rem;
  letter-spacing:.18em;
  text-transform:uppercase;
  color:#a78bfa;
  margin-bottom:.75rem;
}
.ab-07__heading{
  font-size:1.8rem;
  font-weight:800;
  color:#f1f5f9;
  margin-bottom:.75rem;
}
.ab-07__body{
  font-size:.9rem;
  color:#94a3b8;
  margin-bottom:1.75rem;
  line-height:1.6;
}
.ab-07__btn{
  display:inline-block;
  padding:.9rem 2.4rem;
  font-size:1rem;
  font-weight:800;
  color:#fff;
  background:linear-gradient(90deg,#7c3aed,#06b6d4,#f43f5e,#7c3aed);
  background-size:300% 300%;
  border:none;
  border-radius:12px;
  cursor:pointer;
  outline:none;
  letter-spacing:.03em;
  animation:ab-07-shift 4s ease infinite;
  will-change:background-position;
  box-shadow:0 4px 20px rgba(124,58,237,.4);
  transition:filter .2s,box-shadow .2s,transform .15s;
}
.ab-07__btn:hover{
  filter:brightness(1.12);
  box-shadow:0 6px 32px rgba(124,58,237,.6);
  transform:translateY(-2px);
}
.ab-07__btn:active{transform:translateY(0);filter:brightness(.95)}
@keyframes ab-07-shift{
  0%{background-position:0% 50%}
  50%{background-position:100% 50%}
  100%{background-position:0% 50%}
}
@media(prefers-reduced-motion:reduce){
  .ab-07__btn{animation:none;background:linear-gradient(90deg,#7c3aed,#06b6d4)}
}

How this works

The shifting gradient effect is achieved by setting a background-size: 300% 300% on the button and animating background-position with a @keyframes ab-07-shift loop that moves the position from 0% 50% to 100% 50% and back. Because the gradient is three times wider than the visible area, each position reveals a different colour region of the gradient, creating the illusion of a continuously colour-cycling background. The gradient uses stops of violet → cyan → rose → violet so the loop is seamless.

A filter: brightness(1.1) on :hover provides immediate visual feedback without interrupting the gradient animation. The button also carries a matching coloured box-shadow that breathes with the gradient using a second animation applied to the shadow spread. All animation uses only background-position and box-shadow — the former is GPU-accelerated in modern browsers when paired with will-change: background-position.

Customize

  • Swap the colour palette by editing the gradient stops — try #f97316, #ec4899, #8b5cf6 for a sunset feel.
  • Speed up or slow the cycle by changing the animation-duration on .ab-07__btn (default 4s; try 2s for energetic or 8s for ambient).
  • Pause the animation on hover by adding animation-play-state: paused to the :hover rule — this freezes the gradient and creates a "snapshot" effect.
  • Use a diagonal sweep by changing the gradient direction from 90deg to 135deg and keeping the same position animation.
  • Add a shimmer overlay by placing a semi-transparent ::after pseudo-element with a linear-gradient(transparent 40%, rgba(255,255,255,.08) 50%, transparent 60%) animated across the button.

Watch out for

  • background-position animations are not always fully GPU-composited — on battery-constrained devices the animation may consume measurable power; consider pausing with IntersectionObserver when off-screen.
  • Some older WebKit versions render a visible seam at the gradient loop point — ensure the first and last gradient stop colours match (the demo uses violet at both ends) to close the loop.
  • Firefox 68 and below do not support animating background-position smoothly with percentage values on gradients — use explicit pixel positions as a fallback for legacy support.

Browser support

ChromeSafariFirefoxEdge
26+ 9+ 16+ 26+

background-position animation is universally supported; behaviour is consistent across modern browsers.

Search CodeFronts

Loading…