25 CSS Spinners19 / 25
Breathing Circle Loader
A calm azure orb slowly inhales and exhales with a scale pulse while three expanding rings propagate outward in a continuous ripple, creating a meditative loading state.
Published
This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.
The code
<div class="sp-19">
<div class="sp-19__wrap">
<div class="sp-19__ring"></div>
<div class="sp-19__ring"></div>
<div class="sp-19__ring"></div>
<div class="sp-19__core"></div>
</div>
<div class="sp-19__label">Loading</div>
</div><div class="sp-19">
<div class="sp-19__wrap">
<div class="sp-19__ring"></div>
<div class="sp-19__ring"></div>
<div class="sp-19__ring"></div>
<div class="sp-19__core"></div>
</div>
<div class="sp-19__label">Loading</div>
</div>.sp-19,
.sp-19 *,
.sp-19 *::before,
.sp-19 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.sp-19 {
--bg: #03080e;
--c: #4fc3f7;
--glow: rgba(79,195,247,0.3);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
min-height: 100vh;
background: var(--bg);
font-family: system-ui,sans-serif;
}
.sp-19__wrap {
position: relative;
width: 90px;
height: 90px;
}
.sp-19__ring {
position: absolute;
border-radius: 50%;
border: 2px solid var(--c);
opacity: 0;
animation: sp-19-breathe-ring 4s ease-in-out infinite;
}
.sp-19__ring:nth-child(1) {
inset: 0;
}
.sp-19__ring:nth-child(2) {
inset: 0;
animation-delay: 1.3s;
}
.sp-19__ring:nth-child(3) {
inset: 0;
animation-delay: 2.6s;
}
.sp-19__core {
position: absolute;
inset: 20px;
border-radius: 50%;
background: radial-gradient(circle,var(--c),rgba(79,195,247,0.3));
animation: sp-19-breathe-core 4s ease-in-out infinite;
}
.sp-19__label {
color: rgba(79,195,247,0.7);
font-size: 11px;
letter-spacing: 3px;
text-transform: uppercase;
animation: sp-19-text 4s ease-in-out infinite;
}
@keyframes sp-19-breathe-ring {
0% {
transform: scale(0.4);
opacity: 0.8;
}
100% {
transform: scale(1.8);
opacity: 0;
}
}
@keyframes sp-19-breathe-core {
0%,
100% {
transform: scale(0.7);
box-shadow: 0 0 10px var(--glow);
}
50% {
transform: scale(1.15);
box-shadow: 0 0 30px var(--glow),0 0 60px var(--glow);
}
}
@keyframes sp-19-text {
0%,
100% {
opacity: 0.3;
}
50% {
opacity: 1;
}
}
@media (prefers-reduced-motion: reduce) {
.sp-19__ring,
.sp-19__core,
.sp-19__label {
animation: none;
opacity: 1;
}
}.sp-19,
.sp-19 *,
.sp-19 *::before,
.sp-19 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.sp-19 {
--bg: #03080e;
--c: #4fc3f7;
--glow: rgba(79,195,247,0.3);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
min-height: 100vh;
background: var(--bg);
font-family: system-ui,sans-serif;
}
.sp-19__wrap {
position: relative;
width: 90px;
height: 90px;
}
.sp-19__ring {
position: absolute;
border-radius: 50%;
border: 2px solid var(--c);
opacity: 0;
animation: sp-19-breathe-ring 4s ease-in-out infinite;
}
.sp-19__ring:nth-child(1) {
inset: 0;
}
.sp-19__ring:nth-child(2) {
inset: 0;
animation-delay: 1.3s;
}
.sp-19__ring:nth-child(3) {
inset: 0;
animation-delay: 2.6s;
}
.sp-19__core {
position: absolute;
inset: 20px;
border-radius: 50%;
background: radial-gradient(circle,var(--c),rgba(79,195,247,0.3));
animation: sp-19-breathe-core 4s ease-in-out infinite;
}
.sp-19__label {
color: rgba(79,195,247,0.7);
font-size: 11px;
letter-spacing: 3px;
text-transform: uppercase;
animation: sp-19-text 4s ease-in-out infinite;
}
@keyframes sp-19-breathe-ring {
0% {
transform: scale(0.4);
opacity: 0.8;
}
100% {
transform: scale(1.8);
opacity: 0;
}
}
@keyframes sp-19-breathe-core {
0%,
100% {
transform: scale(0.7);
box-shadow: 0 0 10px var(--glow);
}
50% {
transform: scale(1.15);
box-shadow: 0 0 30px var(--glow),0 0 60px var(--glow);
}
}
@keyframes sp-19-text {
0%,
100% {
opacity: 0.3;
}
50% {
opacity: 1;
}
}
@media (prefers-reduced-motion: reduce) {
.sp-19__ring,
.sp-19__core,
.sp-19__label {
animation: none;
opacity: 1;
}
}Here's a working CSS Spinner 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: Breathing Circle Loader
Source: https://codefronts.com/motion/css-spinner-designs/breathing-circle-loader/
A calm azure orb slowly inhales and exhales with a scale pulse while three expanding rings propagate outward in a continuous ripple, creating a meditative loading state.
## HTML
```html
<div class="sp-19">
<div class="sp-19__wrap">
<div class="sp-19__ring"></div>
<div class="sp-19__ring"></div>
<div class="sp-19__ring"></div>
<div class="sp-19__core"></div>
</div>
<div class="sp-19__label">Loading</div>
</div>
```
## CSS
```css
.sp-19,
.sp-19 *,
.sp-19 *::before,
.sp-19 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.sp-19 {
--bg: #03080e;
--c: #4fc3f7;
--glow: rgba(79,195,247,0.3);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
min-height: 100vh;
background: var(--bg);
font-family: system-ui,sans-serif;
}
.sp-19__wrap {
position: relative;
width: 90px;
height: 90px;
}
.sp-19__ring {
position: absolute;
border-radius: 50%;
border: 2px solid var(--c);
opacity: 0;
animation: sp-19-breathe-ring 4s ease-in-out infinite;
}
.sp-19__ring:nth-child(1) {
inset: 0;
}
.sp-19__ring:nth-child(2) {
inset: 0;
animation-delay: 1.3s;
}
.sp-19__ring:nth-child(3) {
inset: 0;
animation-delay: 2.6s;
}
.sp-19__core {
position: absolute;
inset: 20px;
border-radius: 50%;
background: radial-gradient(circle,var(--c),rgba(79,195,247,0.3));
animation: sp-19-breathe-core 4s ease-in-out infinite;
}
.sp-19__label {
color: rgba(79,195,247,0.7);
font-size: 11px;
letter-spacing: 3px;
text-transform: uppercase;
animation: sp-19-text 4s ease-in-out infinite;
}
@keyframes sp-19-breathe-ring {
0% {
transform: scale(0.4);
opacity: 0.8;
}
100% {
transform: scale(1.8);
opacity: 0;
}
}
@keyframes sp-19-breathe-core {
0%,
100% {
transform: scale(0.7);
box-shadow: 0 0 10px var(--glow);
}
50% {
transform: scale(1.15);
box-shadow: 0 0 30px var(--glow),0 0 60px var(--glow);
}
}
@keyframes sp-19-text {
0%,
100% {
opacity: 0.3;
}
50% {
opacity: 1;
}
}
@media (prefers-reduced-motion: reduce) {
.sp-19__ring,
.sp-19__core,
.sp-19__label {
animation: none;
opacity: 1;
}
}
```Here's a working CSS Spinner 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: Breathing Circle Loader
Source: https://codefronts.com/motion/css-spinner-designs/breathing-circle-loader/
A calm azure orb slowly inhales and exhales with a scale pulse while three expanding rings propagate outward in a continuous ripple, creating a meditative loading state.
## HTML
```html
<div class="sp-19">
<div class="sp-19__wrap">
<div class="sp-19__ring"></div>
<div class="sp-19__ring"></div>
<div class="sp-19__ring"></div>
<div class="sp-19__core"></div>
</div>
<div class="sp-19__label">Loading</div>
</div>
```
## CSS
```css
.sp-19,
.sp-19 *,
.sp-19 *::before,
.sp-19 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.sp-19 {
--bg: #03080e;
--c: #4fc3f7;
--glow: rgba(79,195,247,0.3);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
min-height: 100vh;
background: var(--bg);
font-family: system-ui,sans-serif;
}
.sp-19__wrap {
position: relative;
width: 90px;
height: 90px;
}
.sp-19__ring {
position: absolute;
border-radius: 50%;
border: 2px solid var(--c);
opacity: 0;
animation: sp-19-breathe-ring 4s ease-in-out infinite;
}
.sp-19__ring:nth-child(1) {
inset: 0;
}
.sp-19__ring:nth-child(2) {
inset: 0;
animation-delay: 1.3s;
}
.sp-19__ring:nth-child(3) {
inset: 0;
animation-delay: 2.6s;
}
.sp-19__core {
position: absolute;
inset: 20px;
border-radius: 50%;
background: radial-gradient(circle,var(--c),rgba(79,195,247,0.3));
animation: sp-19-breathe-core 4s ease-in-out infinite;
}
.sp-19__label {
color: rgba(79,195,247,0.7);
font-size: 11px;
letter-spacing: 3px;
text-transform: uppercase;
animation: sp-19-text 4s ease-in-out infinite;
}
@keyframes sp-19-breathe-ring {
0% {
transform: scale(0.4);
opacity: 0.8;
}
100% {
transform: scale(1.8);
opacity: 0;
}
}
@keyframes sp-19-breathe-core {
0%,
100% {
transform: scale(0.7);
box-shadow: 0 0 10px var(--glow);
}
50% {
transform: scale(1.15);
box-shadow: 0 0 30px var(--glow),0 0 60px var(--glow);
}
}
@keyframes sp-19-text {
0%,
100% {
opacity: 0.3;
}
50% {
opacity: 1;
}
}
@media (prefers-reduced-motion: reduce) {
.sp-19__ring,
.sp-19__core,
.sp-19__label {
animation: none;
opacity: 1;
}
}
```How this works
The core element runs sp-19-breathe-core: a 4-second ease-in-out cycle from scale(0.7) to scale(1.15) and back, with a simultaneously-animated box-shadow glow that expands from a tight to a diffuse halo — mimicking the glow intensifying on the exhale. The 4-second duration is chosen to match a typical adult breathing cycle, making the animation feel physiologically grounded.
Three rings run sp-19-breathe-ring each starting at scale(0.4) with 0.8 opacity and expanding to scale(1.8) at zero opacity. Offset by 1.3s each (one-third of the 4s period), they create a constant outward propagation that mirrors the core's exhale rhythm.
Make it yours
- Edit
--cto change the orb colour — a warm amber creates a candle-flame breathing loader; deep green suits wellness or eco-themed apps. - Change the duration from
4sto6sfor a slower, deeper breathing pace, or2sfor a quicker cadence suitable as an active spinner. - Increase ring scale from
1.8to2.5for wider propagation — useful over large modal overlays. - Add a text label that fades between "Loading…" and "Please wait…" using a separate opacity keyframe on a sibling element.
- Reduce to one ring by removing two
.sp-19__ringelements for a minimal meditative pulse without the ripple effect.
Gotchas — read before shipping
- The 4s duration with three rings offset by 1.3s leaves a small phase error (3 × 1.3 = 3.9s, not 4s) — rings gradually drift over many cycles; use
1.333sdelay to keep them in phase. box-shadowchanges in keyframes are not composited and trigger a repaint on each frame — switch to a pseudo-element with a scale/opacity animation for a compositor-only approach.- The
Loadingtext label uses letter-spacing animation for effect — this is not compositor-friendly; if FPS is critical, remove the label animation.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 60+ | 12+ | 60+ | 60+ |
Widely supported; box-shadow animation in keyframes triggers repaint but is acceptable for a single-instance spinner.