22 CSS Gradient Buttons03 / 22
CSS Breathing Gradient Button
A mindfulness-app CTA that breathes on a 5-second cycle — the pill gently inhales via a subtle transform:scale(1.045), its gradient slides across itself, and the ambient glow swells and settles. No shape morphing, no label fade, just a calm rhythm matching an app's meditative practice.
Published
The code
<div class="gb-03">
<div class="gb-03__card">
<p class="gb-03__kicker"><span class="gb-03__kicker-dot"></span> Slow product design</p>
<h3 class="gb-03__title">Interfaces that breathe.</h3>
<p class="gb-03__copy">Not every button needs to bark for attention. Some breathe with the user's attention span — inhale, pause, exhale. Ours does it on a five-second cycle when idle.</p>
<button class="gb-03__btn" type="button">
<span class="gb-03__lab">Start your practice</span>
</button>
<p class="gb-03__foot">Hover to hold the breath · pure <code>@keyframes</code> · GPU-only</p>
</div>
</div><div class="gb-03">
<div class="gb-03__card">
<p class="gb-03__kicker"><span class="gb-03__kicker-dot"></span> Slow product design</p>
<h3 class="gb-03__title">Interfaces that breathe.</h3>
<p class="gb-03__copy">Not every button needs to bark for attention. Some breathe with the user's attention span — inhale, pause, exhale. Ours does it on a five-second cycle when idle.</p>
<button class="gb-03__btn" type="button">
<span class="gb-03__lab">Start your practice</span>
</button>
<p class="gb-03__foot">Hover to hold the breath · pure <code>@keyframes</code> · GPU-only</p>
</div>
</div>.gb-03,
.gb-03 *,
.gb-03 *::before,
.gb-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gb-03 ::selection {
background: #67e8f9;
color: #0a1417;
}
.gb-03 {
--bg: #f4f1ea;
--panel: #ffffff;
--line: rgba(41,54,66,.10);
--c1: #5eead4;
--c2: #7c6cff;
--c3: #f0abfc;
--ink: #1d2937;
--mut: #5a6879;
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
background: radial-gradient(60% 80% at 20% 0%, rgba(94,234,212,.16) 0%, transparent 55%), radial-gradient(70% 90% at 100% 100%, rgba(240,171,252,.12) 0%, transparent 60%), var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 48px 24px;
color: var(--ink);
}
.gb-03__card {
width: min(440px,100%);
background: var(--panel);
border: 1px solid var(--line);
border-radius: 20px;
padding: 32px 32px 28px;
box-shadow: 0 30px 60px -30px rgba(41,54,66,.22);
text-align: center;
}
.gb-03__kicker {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 11px;
font-weight: 700;
letter-spacing: .16em;
text-transform: uppercase;
color: #0d9488;
margin-bottom: 14px;
}
.gb-03__kicker-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #a7f3d0, #10b981);
animation: gb-03-dot-pulse 4s ease-in-out infinite;
}
@keyframes gb-03-dot-pulse {
0%,
100% {
transform: scale(1);
opacity: .85;
}
50% {
transform: scale(1.35);
opacity: 1;
}
}
.gb-03__title {
font-family: Georgia,"Times New Roman",serif;
font-size: 26px;
font-weight: 800;
letter-spacing: -.015em;
line-height: 1.15;
margin-bottom: 10px;
color: var(--ink);
}
.gb-03__copy {
font-size: 14px;
line-height: 1.6;
color: var(--mut);
margin-bottom: 32px;
}
.gb-03__btn {
--w: 280px;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--w);
padding: 16px 26px;
border: 0;
border-radius: 999px;
color: #fff;
font-family: inherit;
font-size: 15px;
font-weight: 700;
letter-spacing: .01em;
cursor: pointer;
background: linear-gradient(135deg, var(--c1) 0%, var(--c2) 55%, var(--c3) 100%);
background-size: 200% 200%;
white-space: nowrap;
box-shadow: 0 14px 32px -12px rgba(124,108,255,.45);
animation: gb-03-breathe 5s cubic-bezier(.45,0,.55,1) infinite;
transform-origin: center;
will-change: transform, background-position, filter;
}
.gb-03__btn:focus-visible {
outline: 2px solid var(--ink);
outline-offset: 3px;
}
.gb-03__btn:hover {
animation-play-state: paused;
}
.gb-03__lab {
position: relative;
}
@keyframes gb-03-breathe {
0%,
100% {
transform: scale(1);
background-position: 0% 50%;
filter: hue-rotate(0deg) saturate(1);
box-shadow: 0 14px 32px -12px rgba(124,108,255,.45);
}
50% {
transform: scale(1.045);
background-position: 100% 50%;
filter: hue-rotate(30deg) saturate(1.08);
box-shadow: 0 22px 44px -14px rgba(240,171,252,.5);
}
}
.gb-03__foot {
font-size: 11.5px;
color: var(--mut);
letter-spacing: .02em;
margin-top: 24px;
}
.gb-03__foot code {
font-family: "JetBrains Mono", ui-monospace, monospace;
font-size: 10.5px;
background: rgba(124,108,255,.10);
color: #5b4dff;
padding: 1px 6px;
border-radius: 4px;
}
@media (prefers-reduced-motion: reduce) {
.gb-03__btn {
animation: none;
filter: none;
transform: none;
}
.gb-03__kicker-dot {
animation: none;
}
}.gb-03,
.gb-03 *,
.gb-03 *::before,
.gb-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gb-03 ::selection {
background: #67e8f9;
color: #0a1417;
}
.gb-03 {
--bg: #f4f1ea;
--panel: #ffffff;
--line: rgba(41,54,66,.10);
--c1: #5eead4;
--c2: #7c6cff;
--c3: #f0abfc;
--ink: #1d2937;
--mut: #5a6879;
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
background: radial-gradient(60% 80% at 20% 0%, rgba(94,234,212,.16) 0%, transparent 55%), radial-gradient(70% 90% at 100% 100%, rgba(240,171,252,.12) 0%, transparent 60%), var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 48px 24px;
color: var(--ink);
}
.gb-03__card {
width: min(440px,100%);
background: var(--panel);
border: 1px solid var(--line);
border-radius: 20px;
padding: 32px 32px 28px;
box-shadow: 0 30px 60px -30px rgba(41,54,66,.22);
text-align: center;
}
.gb-03__kicker {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 11px;
font-weight: 700;
letter-spacing: .16em;
text-transform: uppercase;
color: #0d9488;
margin-bottom: 14px;
}
.gb-03__kicker-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #a7f3d0, #10b981);
animation: gb-03-dot-pulse 4s ease-in-out infinite;
}
@keyframes gb-03-dot-pulse {
0%,
100% {
transform: scale(1);
opacity: .85;
}
50% {
transform: scale(1.35);
opacity: 1;
}
}
.gb-03__title {
font-family: Georgia,"Times New Roman",serif;
font-size: 26px;
font-weight: 800;
letter-spacing: -.015em;
line-height: 1.15;
margin-bottom: 10px;
color: var(--ink);
}
.gb-03__copy {
font-size: 14px;
line-height: 1.6;
color: var(--mut);
margin-bottom: 32px;
}
.gb-03__btn {
--w: 280px;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--w);
padding: 16px 26px;
border: 0;
border-radius: 999px;
color: #fff;
font-family: inherit;
font-size: 15px;
font-weight: 700;
letter-spacing: .01em;
cursor: pointer;
background: linear-gradient(135deg, var(--c1) 0%, var(--c2) 55%, var(--c3) 100%);
background-size: 200% 200%;
white-space: nowrap;
box-shadow: 0 14px 32px -12px rgba(124,108,255,.45);
animation: gb-03-breathe 5s cubic-bezier(.45,0,.55,1) infinite;
transform-origin: center;
will-change: transform, background-position, filter;
}
.gb-03__btn:focus-visible {
outline: 2px solid var(--ink);
outline-offset: 3px;
}
.gb-03__btn:hover {
animation-play-state: paused;
}
.gb-03__lab {
position: relative;
}
@keyframes gb-03-breathe {
0%,
100% {
transform: scale(1);
background-position: 0% 50%;
filter: hue-rotate(0deg) saturate(1);
box-shadow: 0 14px 32px -12px rgba(124,108,255,.45);
}
50% {
transform: scale(1.045);
background-position: 100% 50%;
filter: hue-rotate(30deg) saturate(1.08);
box-shadow: 0 22px 44px -14px rgba(240,171,252,.5);
}
}
.gb-03__foot {
font-size: 11.5px;
color: var(--mut);
letter-spacing: .02em;
margin-top: 24px;
}
.gb-03__foot code {
font-family: "JetBrains Mono", ui-monospace, monospace;
font-size: 10.5px;
background: rgba(124,108,255,.10);
color: #5b4dff;
padding: 1px 6px;
border-radius: 4px;
}
@media (prefers-reduced-motion: reduce) {
.gb-03__btn {
animation: none;
filter: none;
transform: none;
}
.gb-03__kicker-dot {
animation: none;
}
}Here's a working CSS Gradient Button from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: CSS Breathing Gradient Button
Source: https://codefronts.com/components/css-gradient-buttons/css-breathing-gradient-button/
A mindfulness-app CTA that breathes on a 5-second cycle — the pill gently inhales via a subtle transform:scale(1.045), its gradient slides across itself, and the ambient glow swells and settles. No shape morphing, no label fade, just a calm rhythm matching an app's meditative practice.
## HTML
```html
<div class="gb-03">
<div class="gb-03__card">
<p class="gb-03__kicker"><span class="gb-03__kicker-dot"></span> Slow product design</p>
<h3 class="gb-03__title">Interfaces that breathe.</h3>
<p class="gb-03__copy">Not every button needs to bark for attention. Some breathe with the user's attention span — inhale, pause, exhale. Ours does it on a five-second cycle when idle.</p>
<button class="gb-03__btn" type="button">
<span class="gb-03__lab">Start your practice</span>
</button>
<p class="gb-03__foot">Hover to hold the breath · pure <code>@keyframes</code> · GPU-only</p>
</div>
</div>
```
## CSS
```css
.gb-03,
.gb-03 *,
.gb-03 *::before,
.gb-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gb-03 ::selection {
background: #67e8f9;
color: #0a1417;
}
.gb-03 {
--bg: #f4f1ea;
--panel: #ffffff;
--line: rgba(41,54,66,.10);
--c1: #5eead4;
--c2: #7c6cff;
--c3: #f0abfc;
--ink: #1d2937;
--mut: #5a6879;
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
background: radial-gradient(60% 80% at 20% 0%, rgba(94,234,212,.16) 0%, transparent 55%), radial-gradient(70% 90% at 100% 100%, rgba(240,171,252,.12) 0%, transparent 60%), var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 48px 24px;
color: var(--ink);
}
.gb-03__card {
width: min(440px,100%);
background: var(--panel);
border: 1px solid var(--line);
border-radius: 20px;
padding: 32px 32px 28px;
box-shadow: 0 30px 60px -30px rgba(41,54,66,.22);
text-align: center;
}
.gb-03__kicker {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 11px;
font-weight: 700;
letter-spacing: .16em;
text-transform: uppercase;
color: #0d9488;
margin-bottom: 14px;
}
.gb-03__kicker-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #a7f3d0, #10b981);
animation: gb-03-dot-pulse 4s ease-in-out infinite;
}
@keyframes gb-03-dot-pulse {
0%,
100% {
transform: scale(1);
opacity: .85;
}
50% {
transform: scale(1.35);
opacity: 1;
}
}
.gb-03__title {
font-family: Georgia,"Times New Roman",serif;
font-size: 26px;
font-weight: 800;
letter-spacing: -.015em;
line-height: 1.15;
margin-bottom: 10px;
color: var(--ink);
}
.gb-03__copy {
font-size: 14px;
line-height: 1.6;
color: var(--mut);
margin-bottom: 32px;
}
.gb-03__btn {
--w: 280px;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--w);
padding: 16px 26px;
border: 0;
border-radius: 999px;
color: #fff;
font-family: inherit;
font-size: 15px;
font-weight: 700;
letter-spacing: .01em;
cursor: pointer;
background: linear-gradient(135deg, var(--c1) 0%, var(--c2) 55%, var(--c3) 100%);
background-size: 200% 200%;
white-space: nowrap;
box-shadow: 0 14px 32px -12px rgba(124,108,255,.45);
animation: gb-03-breathe 5s cubic-bezier(.45,0,.55,1) infinite;
transform-origin: center;
will-change: transform, background-position, filter;
}
.gb-03__btn:focus-visible {
outline: 2px solid var(--ink);
outline-offset: 3px;
}
.gb-03__btn:hover {
animation-play-state: paused;
}
.gb-03__lab {
position: relative;
}
@keyframes gb-03-breathe {
0%,
100% {
transform: scale(1);
background-position: 0% 50%;
filter: hue-rotate(0deg) saturate(1);
box-shadow: 0 14px 32px -12px rgba(124,108,255,.45);
}
50% {
transform: scale(1.045);
background-position: 100% 50%;
filter: hue-rotate(30deg) saturate(1.08);
box-shadow: 0 22px 44px -14px rgba(240,171,252,.5);
}
}
.gb-03__foot {
font-size: 11.5px;
color: var(--mut);
letter-spacing: .02em;
margin-top: 24px;
}
.gb-03__foot code {
font-family: "JetBrains Mono", ui-monospace, monospace;
font-size: 10.5px;
background: rgba(124,108,255,.10);
color: #5b4dff;
padding: 1px 6px;
border-radius: 4px;
}
@media (prefers-reduced-motion: reduce) {
.gb-03__btn {
animation: none;
filter: none;
transform: none;
}
.gb-03__kicker-dot {
animation: none;
}
}
```Here's a working CSS Gradient Button from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: CSS Breathing Gradient Button
Source: https://codefronts.com/components/css-gradient-buttons/css-breathing-gradient-button/
A mindfulness-app CTA that breathes on a 5-second cycle — the pill gently inhales via a subtle transform:scale(1.045), its gradient slides across itself, and the ambient glow swells and settles. No shape morphing, no label fade, just a calm rhythm matching an app's meditative practice.
## HTML
```html
<div class="gb-03">
<div class="gb-03__card">
<p class="gb-03__kicker"><span class="gb-03__kicker-dot"></span> Slow product design</p>
<h3 class="gb-03__title">Interfaces that breathe.</h3>
<p class="gb-03__copy">Not every button needs to bark for attention. Some breathe with the user's attention span — inhale, pause, exhale. Ours does it on a five-second cycle when idle.</p>
<button class="gb-03__btn" type="button">
<span class="gb-03__lab">Start your practice</span>
</button>
<p class="gb-03__foot">Hover to hold the breath · pure <code>@keyframes</code> · GPU-only</p>
</div>
</div>
```
## CSS
```css
.gb-03,
.gb-03 *,
.gb-03 *::before,
.gb-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gb-03 ::selection {
background: #67e8f9;
color: #0a1417;
}
.gb-03 {
--bg: #f4f1ea;
--panel: #ffffff;
--line: rgba(41,54,66,.10);
--c1: #5eead4;
--c2: #7c6cff;
--c3: #f0abfc;
--ink: #1d2937;
--mut: #5a6879;
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
background: radial-gradient(60% 80% at 20% 0%, rgba(94,234,212,.16) 0%, transparent 55%), radial-gradient(70% 90% at 100% 100%, rgba(240,171,252,.12) 0%, transparent 60%), var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 48px 24px;
color: var(--ink);
}
.gb-03__card {
width: min(440px,100%);
background: var(--panel);
border: 1px solid var(--line);
border-radius: 20px;
padding: 32px 32px 28px;
box-shadow: 0 30px 60px -30px rgba(41,54,66,.22);
text-align: center;
}
.gb-03__kicker {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 11px;
font-weight: 700;
letter-spacing: .16em;
text-transform: uppercase;
color: #0d9488;
margin-bottom: 14px;
}
.gb-03__kicker-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #a7f3d0, #10b981);
animation: gb-03-dot-pulse 4s ease-in-out infinite;
}
@keyframes gb-03-dot-pulse {
0%,
100% {
transform: scale(1);
opacity: .85;
}
50% {
transform: scale(1.35);
opacity: 1;
}
}
.gb-03__title {
font-family: Georgia,"Times New Roman",serif;
font-size: 26px;
font-weight: 800;
letter-spacing: -.015em;
line-height: 1.15;
margin-bottom: 10px;
color: var(--ink);
}
.gb-03__copy {
font-size: 14px;
line-height: 1.6;
color: var(--mut);
margin-bottom: 32px;
}
.gb-03__btn {
--w: 280px;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--w);
padding: 16px 26px;
border: 0;
border-radius: 999px;
color: #fff;
font-family: inherit;
font-size: 15px;
font-weight: 700;
letter-spacing: .01em;
cursor: pointer;
background: linear-gradient(135deg, var(--c1) 0%, var(--c2) 55%, var(--c3) 100%);
background-size: 200% 200%;
white-space: nowrap;
box-shadow: 0 14px 32px -12px rgba(124,108,255,.45);
animation: gb-03-breathe 5s cubic-bezier(.45,0,.55,1) infinite;
transform-origin: center;
will-change: transform, background-position, filter;
}
.gb-03__btn:focus-visible {
outline: 2px solid var(--ink);
outline-offset: 3px;
}
.gb-03__btn:hover {
animation-play-state: paused;
}
.gb-03__lab {
position: relative;
}
@keyframes gb-03-breathe {
0%,
100% {
transform: scale(1);
background-position: 0% 50%;
filter: hue-rotate(0deg) saturate(1);
box-shadow: 0 14px 32px -12px rgba(124,108,255,.45);
}
50% {
transform: scale(1.045);
background-position: 100% 50%;
filter: hue-rotate(30deg) saturate(1.08);
box-shadow: 0 22px 44px -14px rgba(240,171,252,.5);
}
}
.gb-03__foot {
font-size: 11.5px;
color: var(--mut);
letter-spacing: .02em;
margin-top: 24px;
}
.gb-03__foot code {
font-family: "JetBrains Mono", ui-monospace, monospace;
font-size: 10.5px;
background: rgba(124,108,255,.10);
color: #5b4dff;
padding: 1px 6px;
border-radius: 4px;
}
@media (prefers-reduced-motion: reduce) {
.gb-03__btn {
animation: none;
filter: none;
transform: none;
}
.gb-03__kicker-dot {
animation: none;
}
}
```How this works
The choreography is a single 5-second @keyframes gb-03-breathe on a symmetric ease that animates three GPU-cheap properties in parallel: transform: scale() from 1 to 1.045 and back (the inhale-exhale), background-position sliding from 0% 50% to 100% 50% across a background-size: 200% 200% gradient (the hue drift through the pastel palette), and filter: hue-rotate(30deg) saturate(1.08) at midpoint (a small warmth shift on the exhale). Box-shadow interpolates in parallel so the coloured ambient glow breathes with the button.
The label stays fully visible and stationary throughout — nothing collapses, nothing fades, nothing shears. The whole button is a stable pill from frame 1 to frame 300; the eye reads the subtle scale + gradient slide as breath, not glitch. All animated properties (transform, background-position, filter) are compositor-friendly so this runs at 60fps on every device with zero paint or layout.
On hover, animation-play-state: paused freezes the button wherever the cursor arrived — so hover feels like the button holding its breath. Release, and it resumes from that exact frame. No JS at all — pure CSS state via :hover.
Make it yours
- Change the breathing tempo by editing the
5sduration on.gb-03__btn— try7sfor meditation-app calm,3sfor workout-app urgency. - Adjust the inhale depth by editing the
scale(1.045)midpoint —1.02is nearly imperceptible,1.08is a strong breath. - Adjust the hue drift by editing
hue-rotate(30deg)at the midpoint —10degkeeps the gradient nearly identical through the cycle,60degmakes the palette-shift dramatic. - Slow the gradient slide independently of the scale by declaring separate animations on
.gb-03__btn::beforefor the background layer. - Recolour by editing
--c1/--c2/--c3. The hue-rotate filter modulates on top of whatever gradient you supply.
Gotchas — read before shipping
filter: hue-rotate()can shift text colour inside the button too — the demo uses white text which is filter-invariant, but coloured labels would drift. If you need coloured labels, animate the gradient stops directly via@property --hue+oklch(60% .18 var(--hue))stops.- Animating
transform: scale()on a button with box-shadow can visibly grow the shadow too. The demo intentionally interpolates the box-shadow along with the scale so shadow growth reads as intentional depth-shift, not scaling artifact. If you prefer a stationary shadow, put the shadow on a wrapper element instead of the button. background-size: 200% 200%is required for the sliding-gradient effect — a normal100% 100%gradient has no unshown territory to slide into, so background-position animation appears static.prefers-reduced-motionis critical for continuous animations — even a gentle 5s breathe can trigger vestibular sensitivity over long exposure. The demo halts the animation and settles to the resting pill state when reduced motion is preferred.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 70+ | 12.1+ | 62+ | 70+ |
The animation uses baseline CSS keyframes + filter — universal support. hue-rotate has been in every browser since 2015.