SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/pulse-animations

7 CSS Pulse Animations

6 Pure CSS1 with JSPublished 2026-10-05Free · no sign-up

Seven pulses that each mean something: a heartbeat with a real double thump, a LIVE badge, placeholder lines that pulse while loading, a breathing exercise circle, a story ring around an avatar, a record button with a timer and a recommended plan whose border glow swells.

01. Heartbeat Pulse

PulseIcon

A heart that beats with the double thump of a real heartbeat: two quick scale-ups close together, then a rest. The rhythm comes from keyframe percentages rather than easing, which is how you get an uneven beat from a single loop.

View code
<p class="pl1"><span class="h" aria-hidden="true">♥</span> Made with care in Leeds</p>
.pl1 { display: flex; align-items: center; gap: 10px; margin: 0; color: #344054; font: 600 16px Inter, system-ui, sans-serif; }
.pl1 .h { display: inline-block; color: #e11d48; font-size: 30px; animation: pl1 1.3s ease-in-out infinite; }
@keyframes pl1 { 0%, 40%, 100% { transform: scale(1); } 10% { transform: scale(1.25); } 20% { transform: scale(1.05); } 30% { transform: scale(1.2); } }
@media (prefers-reduced-motion: reduce) { .pl1 .h { animation: none; } }

02. Live Badge

PulseStatus

A LIVE label whose red dot fades softly in and out, the convention for streams and live events. The pulse is opacity only, which is the gentlest kind of motion, and the word LIVE carries the meaning on its own.

View code
<p class="pl2"><span class="d" aria-hidden="true"></span>LIVE <span class="v">· 1,204 watching</span></p>
.pl2 { display: inline-flex; align-items: center; gap: 8px; margin: 0; padding: 6px 12px; border-radius: 6px; background: #1f1115; color: #fecdd3; font: 800 13px Inter, system-ui, sans-serif; letter-spacing: .08em; }
.pl2 .d { width: 8px; height: 8px; border-radius: 50%; background: #f43f5e; animation: pl2 1.4s ease-in-out infinite; }
.pl2 .v { color: #fda4af; font-weight: 500; letter-spacing: 0; }
@keyframes pl2 { 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) { .pl2 .d { animation: none; } }

03. Pulsing Placeholder Lines

PulseLoading

Grey placeholder bars that pulse between two shades while content loads, a calmer alternative to a shimmer sweep. The block has aria-busy and a hidden Loading message, and each bar has a slightly different width so it looks like text.

View code
<div class="pl3" aria-busy="true"><span class="sr">Loading comments</span><i style="--w:40%"></i><i style="--w:92%"></i><i style="--w:78%"></i><i style="--w:60%"></i></div>
.pl3 { display: grid; gap: 10px; width: 280px; max-width: 100%; padding: 16px; box-sizing: border-box; border: 1px solid #e4e7ec; border-radius: 12px; background: #fff; }
.pl3 i { display: block; width: var(--w); height: 12px; border-radius: 6px; background: #e4e7ec; animation: pl3 1.5s ease-in-out infinite; }
.pl3 i:first-of-type { height: 16px; }
.pl3 .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
@keyframes pl3 { 50% { background: #f2f4f7; } }
@media (prefers-reduced-motion: reduce) { .pl3 i { animation: none; } }

04. Breathing Exercise Circle

PulseWellbeing

A circle that grows for four seconds and shrinks for four, with the words Breathe in and Breathe out cross-fading in time. Both labels use the same eight-second clock, so they stay in sync. The instruction is also written below for anyone with motion turned off.

View code
<div class="pl4"><div class="c" aria-hidden="true"><span class="in">Breathe in</span><span class="out">Breathe out</span></div><p>Breathe in for four seconds, then out for four.</p></div>
.pl4 { display: grid; justify-items: center; gap: 14px; font-family: Inter, system-ui, sans-serif; }
.pl4 .c { display: grid; place-items: center; width: 150px; height: 150px; border-radius: 50%; background: radial-gradient(circle, #5eead4, #0f766e); animation: pl4 8s ease-in-out infinite; }
.pl4 span { grid-area: 1 / 1; color: #042f2e; font-weight: 800; font-size: 15px; }
.pl4 .in { animation: pl4a 8s infinite; } .pl4 .out { animation: pl4b 8s infinite; }
.pl4 p { margin: 0; color: #94a3b8; font-size: 13px; text-align: center; }
@keyframes pl4 { 0%, 100% { transform: scale(.6); } 50% { transform: scale(1); } }
@keyframes pl4a { 0%, 45% { opacity: 1; } 50%, 95% { opacity: 0; } }
@keyframes pl4b { 0%, 45% { opacity: 0; } 50%, 95% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .pl4 .c, .pl4 span { animation: none; } .pl4 .out { opacity: 0; } }

05. Avatar with New Activity Ring

PulseAvatar

A story-style ring around an avatar pulses to show new activity, built from a conic-gradient ring and a slow scale pulse. The ring sits on the link, whose name says Ana has a new story, so the meaning is in words as well as colour.

View code
<a class="pl5" href="#story-ana" aria-label="Ana has a new story"><span class="ring" aria-hidden="true"></span><span class="av" aria-hidden="true">AN</span></a>
.pl5 { position: relative; display: grid; place-items: center; width: 80px; height: 80px; text-decoration: none; }
.pl5 .ring { position: absolute; inset: 0; border-radius: 50%; background: conic-gradient(#f59e0b, #ef4444, #d946ef, #f59e0b); animation: pl5 2s ease-in-out infinite; }
.pl5 .av { position: relative; display: grid; place-items: center; width: 68px; height: 68px; border: 3px solid #fff; border-radius: 50%; background: #fde68a; color: #78350f; font: 800 18px Inter, system-ui, sans-serif; box-sizing: border-box; }
@keyframes pl5 { 50% { transform: scale(1.06); } }
.pl5:focus-visible { outline: 3px solid #528bff; outline-offset: 4px; border-radius: 50%; }
@media (prefers-reduced-motion: reduce) { .pl5 .ring { animation: none; } }

06. Recording Button with Timer

PulseMedia

A record toggle whose red dot pulses while recording and a timer counts the seconds. The button has aria-pressed, its label switches between Start and Stop recording, and the timer is a role="timer" element.

View code
<div class="pl6"><button aria-pressed="false"><span class="d" aria-hidden="true"></span><span class="l">Start recording</span></button><span role="timer" class="t">00:00</span></div>
.pl6 { display: flex; align-items: center; gap: 14px; font-family: Inter, system-ui, sans-serif; }
.pl6 button { display: inline-flex; align-items: center; gap: 10px; padding: 12px 18px; border: 1px solid #3a4670; border-radius: 999px; background: #151b2e; color: #e2e8f0; font: 700 14px Inter, system-ui, sans-serif; cursor: pointer; }
.pl6 .d { width: 14px; height: 14px; border-radius: 50%; background: #ef4444; }
.pl6 [aria-pressed="true"] { border-color: #ef4444; } .pl6 [aria-pressed="true"] .d { animation: pl6 1s ease-in-out infinite; }
.pl6 .t { color: #cbd5e1; font: 600 15px "JetBrains Mono", ui-monospace, monospace; }
.pl6 button:focus-visible { outline: 2px solid #fca5a5; outline-offset: 3px; }
@keyframes pl6 { 50% { transform: scale(.7); opacity: .5; } }
@media (prefers-reduced-motion: reduce) { .pl6 .d { animation: none !important; } }
var b = document.querySelector('.pl6 button'), l = b.querySelector('.l'), t = document.querySelector('.pl6 .t'), s = 0, id;
b.onclick = function () {
  var on = b.getAttribute('aria-pressed') !== 'true'; b.setAttribute('aria-pressed', on); l.textContent = on ? 'Stop recording' : 'Start recording';
  if (on) { s = 0; t.textContent = '00:00'; id = setInterval(function () { s++; t.textContent = String(Math.floor(s / 60)).padStart(2, '0') + ':' + String(s % 60).padStart(2, '0'); }, 1000); } else clearInterval(id);
};

07. Pulsing Border Glow

PulseCard

The recommended plan's border glow gently swells and fades to mark it as the one to choose. The pulse animates a box-shadow on a pseudo-element's opacity, which is cheaper than animating the shadow itself on every frame.

View code
<article class="pl7"><p class="k">Recommended</p><h3>Studio</h3><p>For teams of up to 10.</p></article>
.pl7 { position: relative; width: 220px; max-width: 100%; box-sizing: border-box; padding: 20px; border: 1px solid #6366f1; border-radius: 16px; background: #111527; color: #c7d2fe; font-family: Inter, system-ui, sans-serif; }
.pl7::after { content: ""; position: absolute; inset: -1px; border-radius: inherit; box-shadow: 0 0 24px 2px rgba(99,102,241,.7); opacity: 0; animation: pl7 2.4s ease-in-out infinite; pointer-events: none; }
.pl7 .k { margin: 0; color: #a5b4fc; font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; }
.pl7 h3 { margin: 4px 0; color: #fff; font-size: 22px; } .pl7 p { margin: 0; font-size: 14px; }
@keyframes pl7 { 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .pl7::after { animation: none; opacity: .6; } }

About these CSS Pulse Animations

A pulse says alive or waiting

Pulses work for live status, recording, loading and new activity. They lose meaning when everything pulses, so keep it to one or two elements on a screen.

Rhythm from keyframe percentages

An uneven rhythm, like a heartbeat, comes from where you place keyframes in the loop, not from easing. Two quick peaks at 10 and 30 percent and a long rest until 100 percent give the double thump.

Animate cheap properties

Scale and opacity pulses are smooth everywhere. For a glow, put a fixed box-shadow on a pseudo-element and pulse its opacity, instead of animating the shadow itself.

Words carry the meaning

Pair every pulse with text: LIVE, Recording, Loading comments. Under prefers-reduced-motion the pulses stop and the words remain.

Frequently asked questions

How fast should a pulse be?

About one to two seconds per cycle. Faster feels urgent or alarming; slower feels calm.

Can I sync a label with a pulse?

Yes. Give both the same duration, as the breathing circle does with its two labels.

Do pulses use much battery?

Small transform and opacity pulses are cheap. Stop pulses that are off screen in long pages.