No animations match your search in this category.
The code for all 8 attention, explained
Every effect above, with the markup to use and the complete CSS. Each one is self-contained: paste the CSS once, add the class, done.
Pulse
A gentle, continuous pulse that draws the eye without shouting. Common on live indicators and primary CTAs.
Markup
<button class="fh-pulse">Notifications</button>
CSS
.fh-pulse {
animation: fh-pulse 1.6s ease-in-out infinite;
}
@keyframes fh-pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.07); }
}
Heartbeat
A double-thump rhythm like a real heartbeat. The go-to for like buttons and favourite icons.
Markup
<span class="fh-heartbeat">♥</span>
CSS
.fh-heartbeat {
animation: fh-heartbeat 1.4s ease-in-out infinite;
transform-origin: center;
}
@keyframes fh-heartbeat {
0%, 28%, 70%, 100% { transform: scale(1); }
14% { transform: scale(1.15); }
42% { transform: scale(1.15); }
}
Shake
The universal “no” gesture. Add this class via JavaScript when a form fails validation, then remove it on animationend.
Markup
<input class="fh-shake" /> <!-- add class on invalid submit -->
CSS
.fh-shake {
animation: fh-shake 0.6s ease-in-out both;
}
@keyframes fh-shake {
0%, 100% { transform: translateX(0); }
20%, 60% { transform: translateX(-8px); }
40%, 80% { transform: translateX(8px); }
}
Wobble
Looser and sillier than shake — suits onboarding hints and playful mascots.
Markup
<div class="fh-wobble">Your content</div>
CSS
.fh-wobble {
animation: fh-wobble 0.9s ease-in-out both;
}
@keyframes fh-wobble {
0%, 100% { transform: translateX(0) rotate(0); }
15% { transform: translateX(-14px) rotate(-6deg); }
30% { transform: translateX(10px) rotate(4deg); }
45% { transform: translateX(-8px) rotate(-3deg); }
60% { transform: translateX(5px) rotate(2deg); }
75% { transform: translateX(-2px) rotate(-1deg); }
}
Tada
A celebratory flourish for achievements, confirmations and gamified moments.
Markup
<div class="fh-tada">Achievement unlocked!</div>
CSS
.fh-tada {
animation: fh-tada 0.9s ease-in-out both;
}
@keyframes fh-tada {
0% { transform: scale(1) rotate(0); }
10%, 20% { transform: scale(0.92) rotate(-3deg); }
30%, 50%, 70%, 90% { transform: scale(1.08) rotate(3deg); }
40%, 60%, 80% { transform: scale(1.08) rotate(-3deg); }
100% { transform: scale(1) rotate(0); }
}
Jello
A skew-based jiggle that feels like gelatine. Fun on hover for playful, character-driven brands.
Markup
<div class="fh-jello">Your content</div>
CSS
.fh-jello {
animation: fh-jello 0.9s both;
transform-origin: center;
}
@keyframes fh-jello {
0%, 100% { transform: skew(0, 0); }
22% { transform: skew(-12deg, -12deg); }
33% { transform: skew(9deg, 9deg); }
44% { transform: skew(-6deg, -6deg); }
55% { transform: skew(4deg, 4deg); }
66% { transform: skew(-2deg, -2deg); }
77% { transform: skew(1deg, 1deg); }
}
Swing
Pivots from the top edge like a hanging sign. transform-origin does all the work here.
Markup
<div class="fh-swing">Your content</div>
CSS
.fh-swing {
animation: fh-swing 1s ease-in-out both;
transform-origin: top center;
}
@keyframes fh-swing {
20% { transform: rotate(14deg); }
40% { transform: rotate(-10deg); }
60% { transform: rotate(6deg); }
80% { transform: rotate(-4deg); }
100% { transform: rotate(0); }
}
Bounce
The decaying rebound feels physical because each bounce is smaller than the last. Classic on scroll-down arrows.
Markup
<span class="fh-bounce">↓ Scroll</span>
CSS
.fh-bounce {
animation: fh-bounceY 1s cubic-bezier(0.28, 0.84, 0.42, 1) both;
}
@keyframes fh-bounceY {
0%, 100% { transform: translateY(0); }
25% { transform: translateY(-22px); }
50% { transform: translateY(0); }
70% { transform: translateY(-10px); }
85% { transform: translateY(0); }
95% { transform: translateY(-4px); }
}
Attention animations are a budget you can overspend
These effects work by exploiting the eye's sensitivity to movement — which means their power comes entirely from scarcity. One pulsing element on a screen is impossible to miss; three pulsing elements compete and none of them wins. Treat attention animation as a budget of roughly one per view.
The same logic applies over time. A pulse that runs forever stops being noticed within seconds and becomes background noise, or worse, an irritation the user actively tries to ignore. Loop only where the state genuinely persists — a live indicator, an unread badge — and use finite animations everywhere else.
Matching the effect to the message
Shake is the universal "no": use it for failed validation and rejected actions, where its negative connotation is exactly right. Never use it to draw attention to something positive — the association is too strong.
Pulse and heartbeat read as "alive" or "waiting for you" — good for notification counts, live status and primary calls to action. Tada, jello and wobble are celebratory or playful, which makes them right for success states and character-driven brands, and jarring in serious or professional contexts.
Bounce and swing imply physical weight, which suits scroll indicators and hanging elements where the metaphor makes sense.
Never rely on motion alone
Motion is not an accessible signal by itself. Users who have enabled reduced motion won't see it, screen reader users never do, and anyone glancing away misses it entirely. Every attention animation must be paired with something durable — a text message, a colour change, an icon, a count.
Validation is the clearest example: a field that only shakes tells a screen reader user nothing. Pair it with an error message, aria-invalid and aria-describedby, and the shake becomes a helpful enhancement rather than the entire signal.
Triggering and repeating cleanly
Finite animations only run once per class application. To replay one — a shake on each failed submit — remove the class, force a reflow, then re-add it; or listen for animationend and remove the class there so the next trigger works. Both patterns are shown in the code for each effect.