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.
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> <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)}
} .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, #8b5cf6for a sunset feel. - Speed up or slow the cycle by changing the
animation-durationon.ab-07__btn(default4s; try2sfor energetic or8sfor ambient). - Pause the animation on hover by adding
animation-play-state: pausedto the:hoverrule — this freezes the gradient and creates a "snapshot" effect. - Use a diagonal sweep by changing the gradient direction from
90degto135degand keeping the same position animation. - Add a shimmer overlay by placing a semi-transparent
::afterpseudo-element with alinear-gradient(transparent 40%, rgba(255,255,255,.08) 50%, transparent 60%)animated across the button.
Watch out for
background-positionanimations are not always fully GPU-composited — on battery-constrained devices the animation may consume measurable power; consider pausing withIntersectionObserverwhen 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-positionsmoothly with percentage values on gradients — use explicit pixel positions as a fallback for legacy support.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 26+ | 9+ | 16+ | 26+ |
background-position animation is universally supported; behaviour is consistent across modern browsers.