15 / 30
All 30 in 30 CSS Shake Animations
- 01CSS Shake Animation on Invalid Input
- 02CSS Shake Form on Submit Error
- 03CSS Password Error Shake Effect
- 04CSS Input Field Shake on Wrong Value
- 05Shake Input Box CSS Keyframes
- 06CSS Shake Text on Error
- 07CSS Form Validation Failure Shake
- 08Shake Login Box CSS
- 09CSS Shake Button on Hover
- 10CSS Button Shake on Click
- 11Shake Button CSS Keyframes
- 12CSS Buy Now Button Attention Shake
- 13CSS Subtle Shake Hover Effect
- 14Disabled Button Shake CSS
- 15CSS Notification Bell Shake Animation
- 16CSS Shopping Cart Wiggle on Item Add
- 17CSS Lock Icon Shake Animation
- 18CSS Card Shake Effect on Hover
- 19CSS Alert Badge Shake Animation
- 20CSS Heart Icon Pulse and Shake
- 21CSS Trash Icon Shake on Drag Over
- 22CSS Horizontal Shake Keyframes
- 23CSS Vertical Shake Animation
- 24CSS Skew Shake Animation
- 25CSS Aggressive Shake Animation
- 26CSS Gentle Continuous Shake Animation
- 27CSS Elastic Shake Effect
- 28CSS 3D Shake Transform
- 29CSS Hit Damage Shake Effect for Web Games
- 30CSS Screen Shake Effect Keyframes
CSS Notification Bell Shake Animation
A bell that actually rings, not a div that rotates: the swing pivots from the mount point (transform-origin at the top), decays like real physics, and repeats politely every few seconds while the badge pings — plus a hover variant where the clapper counter-swings against the shell for a two-part mechanical ring.
Build interactive UI like this from scratch: 20 HTML, CSS & JavaScript projects
ZTM · JavaScript Web Projects · 30‑day money-back guarantee · Affiliate link
The code
<div class="shk-15-group">
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@500..800&display=swap" rel="stylesheet">
<section class="shk-15a" aria-label="Notification bell that rings periodically">
<div class="shk-15a__bar">
<span class="shk-15a__brand">north<b>beam</b></span>
<span class="shk-15a__spacer" aria-hidden="true"></span>
<button class="shk-15a__bellbtn" type="button" aria-label="Notifications: 3 unread">
<svg class="shk-15a__bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.7 21a2 2 0 0 1-3.4 0"></path></svg>
<span class="shk-15a__badge" aria-hidden="true">3</span>
<span class="shk-15a__ping" aria-hidden="true"></span>
</button>
<span class="shk-15a__avatar" aria-hidden="true">NB</span>
</div>
<p class="shk-15a__hint" aria-hidden="true">rings every 4s from its mount point — amplitude halves each swing, like a real pendulum</p>
</section>
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400..800&display=swap" rel="stylesheet">
<section class="shk-15b" aria-label="Bell with counter-swinging clapper on hover">
<div class="shk-15b__stage">
<button class="shk-15b__wrap" type="button" aria-label="Ring the bell">
<svg class="shk-15b__svg" viewBox="0 0 48 48" aria-hidden="true">
<g class="shk-15b__shell"><path d="M24 6c-7 0-11 5-11 12 0 9-3.5 13-3.5 13h29S35 27 35 18c0-7-4-12-11-12z" fill="oklch(0.82 0.14 85)" stroke="#3a2c14" stroke-width="2.4" stroke-linejoin="round"></path><circle cx="24" cy="5.4" r="2.3" fill="#3a2c14"></circle></g>
<g class="shk-15b__clap"><line x1="24" y1="22" x2="24" y2="33" stroke="#3a2c14" stroke-width="2.4" stroke-linecap="round"></line><circle cx="24" cy="35.5" r="3.4" fill="#3a2c14"></circle></g>
<path d="M18.5 41.5a5.5 5.5 0 0 0 11 0z" fill="oklch(0.82 0.14 85)" stroke="#3a2c14" stroke-width="2.4" stroke-linejoin="round"></path>
</svg>
</button>
<p class="shk-15b__cap">Hover to ring</p>
<p class="shk-15b__hint" aria-hidden="true">shell and clapper run the same decay keyframes — the clapper's are negated and 40ms late</p>
</div>
</section>
</div><div class="shk-15-group">
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@500..800&display=swap" rel="stylesheet">
<section class="shk-15a" aria-label="Notification bell that rings periodically">
<div class="shk-15a__bar">
<span class="shk-15a__brand">north<b>beam</b></span>
<span class="shk-15a__spacer" aria-hidden="true"></span>
<button class="shk-15a__bellbtn" type="button" aria-label="Notifications: 3 unread">
<svg class="shk-15a__bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.7 21a2 2 0 0 1-3.4 0"></path></svg>
<span class="shk-15a__badge" aria-hidden="true">3</span>
<span class="shk-15a__ping" aria-hidden="true"></span>
</button>
<span class="shk-15a__avatar" aria-hidden="true">NB</span>
</div>
<p class="shk-15a__hint" aria-hidden="true">rings every 4s from its mount point — amplitude halves each swing, like a real pendulum</p>
</section>
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400..800&display=swap" rel="stylesheet">
<section class="shk-15b" aria-label="Bell with counter-swinging clapper on hover">
<div class="shk-15b__stage">
<button class="shk-15b__wrap" type="button" aria-label="Ring the bell">
<svg class="shk-15b__svg" viewBox="0 0 48 48" aria-hidden="true">
<g class="shk-15b__shell"><path d="M24 6c-7 0-11 5-11 12 0 9-3.5 13-3.5 13h29S35 27 35 18c0-7-4-12-11-12z" fill="oklch(0.82 0.14 85)" stroke="#3a2c14" stroke-width="2.4" stroke-linejoin="round"></path><circle cx="24" cy="5.4" r="2.3" fill="#3a2c14"></circle></g>
<g class="shk-15b__clap"><line x1="24" y1="22" x2="24" y2="33" stroke="#3a2c14" stroke-width="2.4" stroke-linecap="round"></line><circle cx="24" cy="35.5" r="3.4" fill="#3a2c14"></circle></g>
<path d="M18.5 41.5a5.5 5.5 0 0 0 11 0z" fill="oklch(0.82 0.14 85)" stroke="#3a2c14" stroke-width="2.4" stroke-linejoin="round"></path>
</svg>
</button>
<p class="shk-15b__cap">Hover to ring</p>
<p class="shk-15b__hint" aria-hidden="true">shell and clapper run the same decay keyframes — the clapper's are negated and 40ms late</p>
</div>
</section>
</div>.shk-15-group {
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 100%;
}
.shk-15-group > section {
flex: 1 1 480px;
min-width: min(100%,360px);
}
.shk-15a,
.shk-15a *,
.shk-15a *::before,
.shk-15a *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.shk-15a {
width: 100%;
min-height: 100vh;
display: grid;
place-content: center;
gap: 22px;
padding: 48px 24px;
background: radial-gradient(120% 100% at 50% 0%,#151a24,#0c0f16);
font-family: 'Manrope','Segoe UI',system-ui,sans-serif;
}
.shk-15a__bar {
display: flex;
align-items: center;
gap: 16px;
width: min(520px,90vw);
padding: 13px 18px;
border-radius: 16px;
background: rgba(255,255,255,.05);
border: 1px solid rgba(160,185,225,.14);
box-shadow: 0 30px 70px -40px rgba(0,0,0,.9);
}
.shk-15a__brand {
font-size: 16px;
font-weight: 500;
letter-spacing: -.01em;
color: rgba(230,238,252,.75);
}
.shk-15a__brand b {
font-weight: 800;
color: #fff;
}
.shk-15a__spacer {
flex: 1;
}
.shk-15a__bellbtn {
position: relative;
width: 44px;
height: 44px;
display: grid;
place-items: center;
border: 0;
border-radius: 12px;
background: rgba(255,255,255,.06);
color: #dfe8f8;
cursor: pointer;
transition: background .2s;
}
.shk-15a__bellbtn:hover {
background: rgba(255,255,255,.12);
}
.shk-15a__bell {
width: 22px;
height: 22px;
transform-origin: 50% 4%;
transform-box: fill-box;
animation: shk-15a-ring 4s ease-in-out infinite;
}
.shk-15a__badge {
position: absolute;
top: 6px;
right: 5px;
min-width: 17px;
height: 17px;
padding: 0 4px;
display: grid;
place-items: center;
font-size: 10.5px;
font-weight: 800;
color: #fff;
background: oklch(0.62 0.22 25);
border-radius: 999px;
animation: shk-15a-pop 4s cubic-bezier(.34,1.56,.64,1) infinite;
}
.shk-15a__ping {
position: absolute;
top: 6px;
right: 5px;
width: 17px;
height: 17px;
border-radius: 999px;
border: 2px solid oklch(0.62 0.22 25);
opacity: 0;
animation: shk-15a-ping 4s ease-out infinite;
}
.shk-15a__avatar {
width: 34px;
height: 34px;
display: grid;
place-items: center;
font-size: 12px;
font-weight: 800;
color: #0c0f16;
background: linear-gradient(140deg,oklch(0.8 0.12 200),oklch(0.7 0.14 260));
border-radius: 50%;
}
.shk-15a__hint {
text-align: center;
font-size: 12px;
color: rgba(223,232,248,.4);
}
@keyframes shk-15a-ring {
0%,
3% {
rotate: 0deg;
}
5% {
rotate: 14deg;
}
9% {
rotate: -12deg;
}
13% {
rotate: 9deg;
}
17% {
rotate: -6deg;
}
21% {
rotate: 3deg;
}
25% {
rotate: -1.5deg;
}
28%,
100% {
rotate: 0deg;
}
}
@keyframes shk-15a-pop {
0%,
4% {
scale: 1;
}
6% {
scale: 1.35;
}
9% {
scale: 1;
}
100% {
scale: 1;
}
}
@keyframes shk-15a-ping {
0%,
4% {
opacity: .8;
scale: 1;
}
20% {
opacity: 0;
scale: 2.2;
}
100% {
opacity: 0;
scale: 2.2;
}
}
@media (prefers-reduced-motion: reduce) {
.shk-15a__bell,
.shk-15a__badge,
.shk-15a__ping {
animation: none;
}
.shk-15a__ping {
opacity: 0;
}
}
.shk-15b,
.shk-15b *,
.shk-15b *::before,
.shk-15b *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.shk-15b {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(170deg,#fdf8ee,#f6ecd8);
font-family: 'Bricolage Grotesque','Segoe UI',system-ui,sans-serif;
}
.shk-15b__stage {
text-align: center;
}
.shk-15b__wrap {
border: 0;
background: #fff;
cursor: pointer;
width: 150px;
height: 150px;
display: grid;
place-items: center;
border-radius: 36px;
border: 2.5px solid #3a2c14;
box-shadow: 7px 7px 0 #3a2c14;
transition: translate .15s,box-shadow .15s;
}
.shk-15b__wrap:active {
translate: 3px 3px;
box-shadow: 3px 3px 0 #3a2c14;
}
.shk-15b__wrap:focus-visible {
outline: 3px solid oklch(0.7 0.14 85);
outline-offset: 4px;
}
.shk-15b__svg {
width: 84px;
height: 84px;
overflow: visible;
}
.shk-15b__shell,
.shk-15b__clap {
transform-origin: 24px 5px;
transform-box: view-box;
}
.shk-15b__wrap:hover .shk-15b__shell {
animation: shk-15b-shell 1.1s ease-in-out infinite;
}
.shk-15b__wrap:hover .shk-15b__clap {
animation: shk-15b-clap 1.1s ease-in-out .04s infinite;
}
.shk-15b__cap {
margin-top: 20px;
font-size: 16px;
font-weight: 700;
color: #3a2c14;
}
.shk-15b__hint {
margin-top: 8px;
font-size: 12px;
color: rgba(58,44,20,.5);
max-width: 340px;
}
@keyframes shk-15b-shell {
0%,
100% {
rotate: 0deg;
}
12% {
rotate: 13deg;
}
30% {
rotate: -11deg;
}
48% {
rotate: 8deg;
}
64% {
rotate: -5deg;
}
80% {
rotate: 2deg;
}
}
@keyframes shk-15b-clap {
0%,
100% {
rotate: 0deg;
}
12% {
rotate: -16deg;
}
30% {
rotate: 14deg;
}
48% {
rotate: -10deg;
}
64% {
rotate: 6deg;
}
80% {
rotate: -3deg;
}
}
@media (prefers-reduced-motion: reduce) {
.shk-15b__wrap:hover .shk-15b__shell,
.shk-15b__wrap:hover .shk-15b__clap {
animation: none;
}
}.shk-15-group {
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 100%;
}
.shk-15-group > section {
flex: 1 1 480px;
min-width: min(100%,360px);
}
.shk-15a,
.shk-15a *,
.shk-15a *::before,
.shk-15a *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.shk-15a {
width: 100%;
min-height: 100vh;
display: grid;
place-content: center;
gap: 22px;
padding: 48px 24px;
background: radial-gradient(120% 100% at 50% 0%,#151a24,#0c0f16);
font-family: 'Manrope','Segoe UI',system-ui,sans-serif;
}
.shk-15a__bar {
display: flex;
align-items: center;
gap: 16px;
width: min(520px,90vw);
padding: 13px 18px;
border-radius: 16px;
background: rgba(255,255,255,.05);
border: 1px solid rgba(160,185,225,.14);
box-shadow: 0 30px 70px -40px rgba(0,0,0,.9);
}
.shk-15a__brand {
font-size: 16px;
font-weight: 500;
letter-spacing: -.01em;
color: rgba(230,238,252,.75);
}
.shk-15a__brand b {
font-weight: 800;
color: #fff;
}
.shk-15a__spacer {
flex: 1;
}
.shk-15a__bellbtn {
position: relative;
width: 44px;
height: 44px;
display: grid;
place-items: center;
border: 0;
border-radius: 12px;
background: rgba(255,255,255,.06);
color: #dfe8f8;
cursor: pointer;
transition: background .2s;
}
.shk-15a__bellbtn:hover {
background: rgba(255,255,255,.12);
}
.shk-15a__bell {
width: 22px;
height: 22px;
transform-origin: 50% 4%;
transform-box: fill-box;
animation: shk-15a-ring 4s ease-in-out infinite;
}
.shk-15a__badge {
position: absolute;
top: 6px;
right: 5px;
min-width: 17px;
height: 17px;
padding: 0 4px;
display: grid;
place-items: center;
font-size: 10.5px;
font-weight: 800;
color: #fff;
background: oklch(0.62 0.22 25);
border-radius: 999px;
animation: shk-15a-pop 4s cubic-bezier(.34,1.56,.64,1) infinite;
}
.shk-15a__ping {
position: absolute;
top: 6px;
right: 5px;
width: 17px;
height: 17px;
border-radius: 999px;
border: 2px solid oklch(0.62 0.22 25);
opacity: 0;
animation: shk-15a-ping 4s ease-out infinite;
}
.shk-15a__avatar {
width: 34px;
height: 34px;
display: grid;
place-items: center;
font-size: 12px;
font-weight: 800;
color: #0c0f16;
background: linear-gradient(140deg,oklch(0.8 0.12 200),oklch(0.7 0.14 260));
border-radius: 50%;
}
.shk-15a__hint {
text-align: center;
font-size: 12px;
color: rgba(223,232,248,.4);
}
@keyframes shk-15a-ring {
0%,
3% {
rotate: 0deg;
}
5% {
rotate: 14deg;
}
9% {
rotate: -12deg;
}
13% {
rotate: 9deg;
}
17% {
rotate: -6deg;
}
21% {
rotate: 3deg;
}
25% {
rotate: -1.5deg;
}
28%,
100% {
rotate: 0deg;
}
}
@keyframes shk-15a-pop {
0%,
4% {
scale: 1;
}
6% {
scale: 1.35;
}
9% {
scale: 1;
}
100% {
scale: 1;
}
}
@keyframes shk-15a-ping {
0%,
4% {
opacity: .8;
scale: 1;
}
20% {
opacity: 0;
scale: 2.2;
}
100% {
opacity: 0;
scale: 2.2;
}
}
@media (prefers-reduced-motion: reduce) {
.shk-15a__bell,
.shk-15a__badge,
.shk-15a__ping {
animation: none;
}
.shk-15a__ping {
opacity: 0;
}
}
.shk-15b,
.shk-15b *,
.shk-15b *::before,
.shk-15b *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.shk-15b {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(170deg,#fdf8ee,#f6ecd8);
font-family: 'Bricolage Grotesque','Segoe UI',system-ui,sans-serif;
}
.shk-15b__stage {
text-align: center;
}
.shk-15b__wrap {
border: 0;
background: #fff;
cursor: pointer;
width: 150px;
height: 150px;
display: grid;
place-items: center;
border-radius: 36px;
border: 2.5px solid #3a2c14;
box-shadow: 7px 7px 0 #3a2c14;
transition: translate .15s,box-shadow .15s;
}
.shk-15b__wrap:active {
translate: 3px 3px;
box-shadow: 3px 3px 0 #3a2c14;
}
.shk-15b__wrap:focus-visible {
outline: 3px solid oklch(0.7 0.14 85);
outline-offset: 4px;
}
.shk-15b__svg {
width: 84px;
height: 84px;
overflow: visible;
}
.shk-15b__shell,
.shk-15b__clap {
transform-origin: 24px 5px;
transform-box: view-box;
}
.shk-15b__wrap:hover .shk-15b__shell {
animation: shk-15b-shell 1.1s ease-in-out infinite;
}
.shk-15b__wrap:hover .shk-15b__clap {
animation: shk-15b-clap 1.1s ease-in-out .04s infinite;
}
.shk-15b__cap {
margin-top: 20px;
font-size: 16px;
font-weight: 700;
color: #3a2c14;
}
.shk-15b__hint {
margin-top: 8px;
font-size: 12px;
color: rgba(58,44,20,.5);
max-width: 340px;
}
@keyframes shk-15b-shell {
0%,
100% {
rotate: 0deg;
}
12% {
rotate: 13deg;
}
30% {
rotate: -11deg;
}
48% {
rotate: 8deg;
}
64% {
rotate: -5deg;
}
80% {
rotate: 2deg;
}
}
@keyframes shk-15b-clap {
0%,
100% {
rotate: 0deg;
}
12% {
rotate: -16deg;
}
30% {
rotate: 14deg;
}
48% {
rotate: -10deg;
}
64% {
rotate: 6deg;
}
80% {
rotate: -3deg;
}
}
@media (prefers-reduced-motion: reduce) {
.shk-15b__wrap:hover .shk-15b__shell,
.shk-15b__wrap:hover .shk-15b__clap {
animation: none;
}
}/* No JavaScript — bell, badge pop and ping ring share one 4s timeline (burst in the first 28%), so count and motion always arrive together. transform-box:fill-box makes the 50% 4% origin resolve against the bell path, not the whole SVG canvas. */ /* No JavaScript — two SVG groups pivot on the same mount point; the clapper mirrors the shell's frames with opposite signs, slightly larger angles and a 40ms delay, which is exactly how a physical bell moves. */
/* No JavaScript — bell, badge pop and ping ring share one 4s timeline (burst in the first 28%), so count and motion always arrive together. transform-box:fill-box makes the 50% 4% origin resolve against the bell path, not the whole SVG canvas. */
/* No JavaScript — two SVG groups pivot on the same mount point; the clapper mirrors the shell's frames with opposite signs, slightly larger angles and a 40ms delay, which is exactly how a physical bell moves. */Here's a working CSS Shake Animation 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 Notification Bell Shake Animation
Source: https://codefronts.com/motion/css-shake-animation/css-notification-bell-shake-animation/
A bell that actually rings, not a div that rotates: the swing pivots from the mount point (transform-origin at the top), decays like real physics, and repeats politely every few seconds while the badge pings — plus a hover variant where the clapper counter-swings against the shell for a two-part mechanical ring.
## HTML
```html
<div class="shk-15-group">
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@500..800&display=swap" rel="stylesheet">
<section class="shk-15a" aria-label="Notification bell that rings periodically">
<div class="shk-15a__bar">
<span class="shk-15a__brand">north<b>beam</b></span>
<span class="shk-15a__spacer" aria-hidden="true"></span>
<button class="shk-15a__bellbtn" type="button" aria-label="Notifications: 3 unread">
<svg class="shk-15a__bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.7 21a2 2 0 0 1-3.4 0"></path></svg>
<span class="shk-15a__badge" aria-hidden="true">3</span>
<span class="shk-15a__ping" aria-hidden="true"></span>
</button>
<span class="shk-15a__avatar" aria-hidden="true">NB</span>
</div>
<p class="shk-15a__hint" aria-hidden="true">rings every 4s from its mount point — amplitude halves each swing, like a real pendulum</p>
</section>
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400..800&display=swap" rel="stylesheet">
<section class="shk-15b" aria-label="Bell with counter-swinging clapper on hover">
<div class="shk-15b__stage">
<button class="shk-15b__wrap" type="button" aria-label="Ring the bell">
<svg class="shk-15b__svg" viewBox="0 0 48 48" aria-hidden="true">
<g class="shk-15b__shell"><path d="M24 6c-7 0-11 5-11 12 0 9-3.5 13-3.5 13h29S35 27 35 18c0-7-4-12-11-12z" fill="oklch(0.82 0.14 85)" stroke="#3a2c14" stroke-width="2.4" stroke-linejoin="round"></path><circle cx="24" cy="5.4" r="2.3" fill="#3a2c14"></circle></g>
<g class="shk-15b__clap"><line x1="24" y1="22" x2="24" y2="33" stroke="#3a2c14" stroke-width="2.4" stroke-linecap="round"></line><circle cx="24" cy="35.5" r="3.4" fill="#3a2c14"></circle></g>
<path d="M18.5 41.5a5.5 5.5 0 0 0 11 0z" fill="oklch(0.82 0.14 85)" stroke="#3a2c14" stroke-width="2.4" stroke-linejoin="round"></path>
</svg>
</button>
<p class="shk-15b__cap">Hover to ring</p>
<p class="shk-15b__hint" aria-hidden="true">shell and clapper run the same decay keyframes — the clapper's are negated and 40ms late</p>
</div>
</section>
</div>
```
## CSS
```css
.shk-15-group {
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 100%;
}
.shk-15-group > section {
flex: 1 1 480px;
min-width: min(100%,360px);
}
.shk-15a,
.shk-15a *,
.shk-15a *::before,
.shk-15a *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.shk-15a {
width: 100%;
min-height: 100vh;
display: grid;
place-content: center;
gap: 22px;
padding: 48px 24px;
background: radial-gradient(120% 100% at 50% 0%,#151a24,#0c0f16);
font-family: 'Manrope','Segoe UI',system-ui,sans-serif;
}
.shk-15a__bar {
display: flex;
align-items: center;
gap: 16px;
width: min(520px,90vw);
padding: 13px 18px;
border-radius: 16px;
background: rgba(255,255,255,.05);
border: 1px solid rgba(160,185,225,.14);
box-shadow: 0 30px 70px -40px rgba(0,0,0,.9);
}
.shk-15a__brand {
font-size: 16px;
font-weight: 500;
letter-spacing: -.01em;
color: rgba(230,238,252,.75);
}
.shk-15a__brand b {
font-weight: 800;
color: #fff;
}
.shk-15a__spacer {
flex: 1;
}
.shk-15a__bellbtn {
position: relative;
width: 44px;
height: 44px;
display: grid;
place-items: center;
border: 0;
border-radius: 12px;
background: rgba(255,255,255,.06);
color: #dfe8f8;
cursor: pointer;
transition: background .2s;
}
.shk-15a__bellbtn:hover {
background: rgba(255,255,255,.12);
}
.shk-15a__bell {
width: 22px;
height: 22px;
transform-origin: 50% 4%;
transform-box: fill-box;
animation: shk-15a-ring 4s ease-in-out infinite;
}
.shk-15a__badge {
position: absolute;
top: 6px;
right: 5px;
min-width: 17px;
height: 17px;
padding: 0 4px;
display: grid;
place-items: center;
font-size: 10.5px;
font-weight: 800;
color: #fff;
background: oklch(0.62 0.22 25);
border-radius: 999px;
animation: shk-15a-pop 4s cubic-bezier(.34,1.56,.64,1) infinite;
}
.shk-15a__ping {
position: absolute;
top: 6px;
right: 5px;
width: 17px;
height: 17px;
border-radius: 999px;
border: 2px solid oklch(0.62 0.22 25);
opacity: 0;
animation: shk-15a-ping 4s ease-out infinite;
}
.shk-15a__avatar {
width: 34px;
height: 34px;
display: grid;
place-items: center;
font-size: 12px;
font-weight: 800;
color: #0c0f16;
background: linear-gradient(140deg,oklch(0.8 0.12 200),oklch(0.7 0.14 260));
border-radius: 50%;
}
.shk-15a__hint {
text-align: center;
font-size: 12px;
color: rgba(223,232,248,.4);
}
@keyframes shk-15a-ring {
0%,
3% {
rotate: 0deg;
}
5% {
rotate: 14deg;
}
9% {
rotate: -12deg;
}
13% {
rotate: 9deg;
}
17% {
rotate: -6deg;
}
21% {
rotate: 3deg;
}
25% {
rotate: -1.5deg;
}
28%,
100% {
rotate: 0deg;
}
}
@keyframes shk-15a-pop {
0%,
4% {
scale: 1;
}
6% {
scale: 1.35;
}
9% {
scale: 1;
}
100% {
scale: 1;
}
}
@keyframes shk-15a-ping {
0%,
4% {
opacity: .8;
scale: 1;
}
20% {
opacity: 0;
scale: 2.2;
}
100% {
opacity: 0;
scale: 2.2;
}
}
@media (prefers-reduced-motion: reduce) {
.shk-15a__bell,
.shk-15a__badge,
.shk-15a__ping {
animation: none;
}
.shk-15a__ping {
opacity: 0;
}
}
.shk-15b,
.shk-15b *,
.shk-15b *::before,
.shk-15b *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.shk-15b {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(170deg,#fdf8ee,#f6ecd8);
font-family: 'Bricolage Grotesque','Segoe UI',system-ui,sans-serif;
}
.shk-15b__stage {
text-align: center;
}
.shk-15b__wrap {
border: 0;
background: #fff;
cursor: pointer;
width: 150px;
height: 150px;
display: grid;
place-items: center;
border-radius: 36px;
border: 2.5px solid #3a2c14;
box-shadow: 7px 7px 0 #3a2c14;
transition: translate .15s,box-shadow .15s;
}
.shk-15b__wrap:active {
translate: 3px 3px;
box-shadow: 3px 3px 0 #3a2c14;
}
.shk-15b__wrap:focus-visible {
outline: 3px solid oklch(0.7 0.14 85);
outline-offset: 4px;
}
.shk-15b__svg {
width: 84px;
height: 84px;
overflow: visible;
}
.shk-15b__shell,
.shk-15b__clap {
transform-origin: 24px 5px;
transform-box: view-box;
}
.shk-15b__wrap:hover .shk-15b__shell {
animation: shk-15b-shell 1.1s ease-in-out infinite;
}
.shk-15b__wrap:hover .shk-15b__clap {
animation: shk-15b-clap 1.1s ease-in-out .04s infinite;
}
.shk-15b__cap {
margin-top: 20px;
font-size: 16px;
font-weight: 700;
color: #3a2c14;
}
.shk-15b__hint {
margin-top: 8px;
font-size: 12px;
color: rgba(58,44,20,.5);
max-width: 340px;
}
@keyframes shk-15b-shell {
0%,
100% {
rotate: 0deg;
}
12% {
rotate: 13deg;
}
30% {
rotate: -11deg;
}
48% {
rotate: 8deg;
}
64% {
rotate: -5deg;
}
80% {
rotate: 2deg;
}
}
@keyframes shk-15b-clap {
0%,
100% {
rotate: 0deg;
}
12% {
rotate: -16deg;
}
30% {
rotate: 14deg;
}
48% {
rotate: -10deg;
}
64% {
rotate: 6deg;
}
80% {
rotate: -3deg;
}
}
@media (prefers-reduced-motion: reduce) {
.shk-15b__wrap:hover .shk-15b__shell,
.shk-15b__wrap:hover .shk-15b__clap {
animation: none;
}
}
```
## JavaScript
```js
/* No JavaScript — bell, badge pop and ping ring share one 4s timeline (burst in the first 28%), so count and motion always arrive together. transform-box:fill-box makes the 50% 4% origin resolve against the bell path, not the whole SVG canvas. */
/* No JavaScript — two SVG groups pivot on the same mount point; the clapper mirrors the shell's frames with opposite signs, slightly larger angles and a 40ms delay, which is exactly how a physical bell moves. */
```Here's a working CSS Shake Animation 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 Notification Bell Shake Animation
Source: https://codefronts.com/motion/css-shake-animation/css-notification-bell-shake-animation/
A bell that actually rings, not a div that rotates: the swing pivots from the mount point (transform-origin at the top), decays like real physics, and repeats politely every few seconds while the badge pings — plus a hover variant where the clapper counter-swings against the shell for a two-part mechanical ring.
## HTML
```html
<div class="shk-15-group">
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@500..800&display=swap" rel="stylesheet">
<section class="shk-15a" aria-label="Notification bell that rings periodically">
<div class="shk-15a__bar">
<span class="shk-15a__brand">north<b>beam</b></span>
<span class="shk-15a__spacer" aria-hidden="true"></span>
<button class="shk-15a__bellbtn" type="button" aria-label="Notifications: 3 unread">
<svg class="shk-15a__bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.7 21a2 2 0 0 1-3.4 0"></path></svg>
<span class="shk-15a__badge" aria-hidden="true">3</span>
<span class="shk-15a__ping" aria-hidden="true"></span>
</button>
<span class="shk-15a__avatar" aria-hidden="true">NB</span>
</div>
<p class="shk-15a__hint" aria-hidden="true">rings every 4s from its mount point — amplitude halves each swing, like a real pendulum</p>
</section>
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400..800&display=swap" rel="stylesheet">
<section class="shk-15b" aria-label="Bell with counter-swinging clapper on hover">
<div class="shk-15b__stage">
<button class="shk-15b__wrap" type="button" aria-label="Ring the bell">
<svg class="shk-15b__svg" viewBox="0 0 48 48" aria-hidden="true">
<g class="shk-15b__shell"><path d="M24 6c-7 0-11 5-11 12 0 9-3.5 13-3.5 13h29S35 27 35 18c0-7-4-12-11-12z" fill="oklch(0.82 0.14 85)" stroke="#3a2c14" stroke-width="2.4" stroke-linejoin="round"></path><circle cx="24" cy="5.4" r="2.3" fill="#3a2c14"></circle></g>
<g class="shk-15b__clap"><line x1="24" y1="22" x2="24" y2="33" stroke="#3a2c14" stroke-width="2.4" stroke-linecap="round"></line><circle cx="24" cy="35.5" r="3.4" fill="#3a2c14"></circle></g>
<path d="M18.5 41.5a5.5 5.5 0 0 0 11 0z" fill="oklch(0.82 0.14 85)" stroke="#3a2c14" stroke-width="2.4" stroke-linejoin="round"></path>
</svg>
</button>
<p class="shk-15b__cap">Hover to ring</p>
<p class="shk-15b__hint" aria-hidden="true">shell and clapper run the same decay keyframes — the clapper's are negated and 40ms late</p>
</div>
</section>
</div>
```
## CSS
```css
.shk-15-group {
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 100%;
}
.shk-15-group > section {
flex: 1 1 480px;
min-width: min(100%,360px);
}
.shk-15a,
.shk-15a *,
.shk-15a *::before,
.shk-15a *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.shk-15a {
width: 100%;
min-height: 100vh;
display: grid;
place-content: center;
gap: 22px;
padding: 48px 24px;
background: radial-gradient(120% 100% at 50% 0%,#151a24,#0c0f16);
font-family: 'Manrope','Segoe UI',system-ui,sans-serif;
}
.shk-15a__bar {
display: flex;
align-items: center;
gap: 16px;
width: min(520px,90vw);
padding: 13px 18px;
border-radius: 16px;
background: rgba(255,255,255,.05);
border: 1px solid rgba(160,185,225,.14);
box-shadow: 0 30px 70px -40px rgba(0,0,0,.9);
}
.shk-15a__brand {
font-size: 16px;
font-weight: 500;
letter-spacing: -.01em;
color: rgba(230,238,252,.75);
}
.shk-15a__brand b {
font-weight: 800;
color: #fff;
}
.shk-15a__spacer {
flex: 1;
}
.shk-15a__bellbtn {
position: relative;
width: 44px;
height: 44px;
display: grid;
place-items: center;
border: 0;
border-radius: 12px;
background: rgba(255,255,255,.06);
color: #dfe8f8;
cursor: pointer;
transition: background .2s;
}
.shk-15a__bellbtn:hover {
background: rgba(255,255,255,.12);
}
.shk-15a__bell {
width: 22px;
height: 22px;
transform-origin: 50% 4%;
transform-box: fill-box;
animation: shk-15a-ring 4s ease-in-out infinite;
}
.shk-15a__badge {
position: absolute;
top: 6px;
right: 5px;
min-width: 17px;
height: 17px;
padding: 0 4px;
display: grid;
place-items: center;
font-size: 10.5px;
font-weight: 800;
color: #fff;
background: oklch(0.62 0.22 25);
border-radius: 999px;
animation: shk-15a-pop 4s cubic-bezier(.34,1.56,.64,1) infinite;
}
.shk-15a__ping {
position: absolute;
top: 6px;
right: 5px;
width: 17px;
height: 17px;
border-radius: 999px;
border: 2px solid oklch(0.62 0.22 25);
opacity: 0;
animation: shk-15a-ping 4s ease-out infinite;
}
.shk-15a__avatar {
width: 34px;
height: 34px;
display: grid;
place-items: center;
font-size: 12px;
font-weight: 800;
color: #0c0f16;
background: linear-gradient(140deg,oklch(0.8 0.12 200),oklch(0.7 0.14 260));
border-radius: 50%;
}
.shk-15a__hint {
text-align: center;
font-size: 12px;
color: rgba(223,232,248,.4);
}
@keyframes shk-15a-ring {
0%,
3% {
rotate: 0deg;
}
5% {
rotate: 14deg;
}
9% {
rotate: -12deg;
}
13% {
rotate: 9deg;
}
17% {
rotate: -6deg;
}
21% {
rotate: 3deg;
}
25% {
rotate: -1.5deg;
}
28%,
100% {
rotate: 0deg;
}
}
@keyframes shk-15a-pop {
0%,
4% {
scale: 1;
}
6% {
scale: 1.35;
}
9% {
scale: 1;
}
100% {
scale: 1;
}
}
@keyframes shk-15a-ping {
0%,
4% {
opacity: .8;
scale: 1;
}
20% {
opacity: 0;
scale: 2.2;
}
100% {
opacity: 0;
scale: 2.2;
}
}
@media (prefers-reduced-motion: reduce) {
.shk-15a__bell,
.shk-15a__badge,
.shk-15a__ping {
animation: none;
}
.shk-15a__ping {
opacity: 0;
}
}
.shk-15b,
.shk-15b *,
.shk-15b *::before,
.shk-15b *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.shk-15b {
width: 100%;
min-height: 100vh;
display: grid;
place-items: center;
padding: 48px 24px;
background: linear-gradient(170deg,#fdf8ee,#f6ecd8);
font-family: 'Bricolage Grotesque','Segoe UI',system-ui,sans-serif;
}
.shk-15b__stage {
text-align: center;
}
.shk-15b__wrap {
border: 0;
background: #fff;
cursor: pointer;
width: 150px;
height: 150px;
display: grid;
place-items: center;
border-radius: 36px;
border: 2.5px solid #3a2c14;
box-shadow: 7px 7px 0 #3a2c14;
transition: translate .15s,box-shadow .15s;
}
.shk-15b__wrap:active {
translate: 3px 3px;
box-shadow: 3px 3px 0 #3a2c14;
}
.shk-15b__wrap:focus-visible {
outline: 3px solid oklch(0.7 0.14 85);
outline-offset: 4px;
}
.shk-15b__svg {
width: 84px;
height: 84px;
overflow: visible;
}
.shk-15b__shell,
.shk-15b__clap {
transform-origin: 24px 5px;
transform-box: view-box;
}
.shk-15b__wrap:hover .shk-15b__shell {
animation: shk-15b-shell 1.1s ease-in-out infinite;
}
.shk-15b__wrap:hover .shk-15b__clap {
animation: shk-15b-clap 1.1s ease-in-out .04s infinite;
}
.shk-15b__cap {
margin-top: 20px;
font-size: 16px;
font-weight: 700;
color: #3a2c14;
}
.shk-15b__hint {
margin-top: 8px;
font-size: 12px;
color: rgba(58,44,20,.5);
max-width: 340px;
}
@keyframes shk-15b-shell {
0%,
100% {
rotate: 0deg;
}
12% {
rotate: 13deg;
}
30% {
rotate: -11deg;
}
48% {
rotate: 8deg;
}
64% {
rotate: -5deg;
}
80% {
rotate: 2deg;
}
}
@keyframes shk-15b-clap {
0%,
100% {
rotate: 0deg;
}
12% {
rotate: -16deg;
}
30% {
rotate: 14deg;
}
48% {
rotate: -10deg;
}
64% {
rotate: 6deg;
}
80% {
rotate: -3deg;
}
}
@media (prefers-reduced-motion: reduce) {
.shk-15b__wrap:hover .shk-15b__shell,
.shk-15b__wrap:hover .shk-15b__clap {
animation: none;
}
}
```
## JavaScript
```js
/* No JavaScript — bell, badge pop and ping ring share one 4s timeline (burst in the first 28%), so count and motion always arrive together. transform-box:fill-box makes the 50% 4% origin resolve against the bell path, not the whole SVG canvas. */
/* No JavaScript — two SVG groups pivot on the same mount point; the clapper mirrors the shell's frames with opposite signs, slightly larger angles and a 40ms delay, which is exactly how a physical bell moves. */
```How this works
What separates a ring from a wobble is the pivot. Bells hang — so the rotation origin goes where the bell meets its mount:
.bell{transform-origin:50% 4%;animation:ring 4s ease-in-out infinite}
@keyframes ring{
0%,3%{rotate:0deg}
5%{rotate:14deg} 9%{rotate:-12deg}
13%{rotate:9deg} 17%{rotate:-6deg}
21%{rotate:3deg} 25%{rotate:-1.5deg}
28%,100%{rotate:0deg}
}Amplitude halves every swing — the decay curve of a real pendulum. The clapper variant animates a second SVG group with the same frames negated and delayed ~40ms; the shell and clapper moving in opposition is what your eye knows from physical bells. The badge pop rides the same timeline so count and motion arrive as one event.
Make it yours
- Origin 50% 0–8%: lower origins (20%+) turn the ring into a drunken wobble.
- First swing 12–16° for a headline bell, 8° for a 20px toolbar icon.
- Trigger on state change (class) when a real notification lands; the idle loop is for demo/attention surfaces.
- Delay the badge pop to the first swing peak (~5% of timeline) so the count appears mid-ring.
Gotchas — read before shipping
- SVG transform-origin behaves per SVG coordinate system — set transform-box:fill-box on the animated group or origins compute against the whole viewBox.
- Don't rotate the badge with the bell (badges are counters, not clappers) — keep it outside the swinging group.
- Screen readers need the count in text (aria-label='3 unread notifications'), never only the visual badge.
- A bell that rings forever trains users to ignore it — cap at 2–3 rings per new event.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 15.4+ | 113+ | 111+ |
Floor set by oklch() as this demo is written. The core technique itself goes back further — Chrome all modern.
transform-box:fill-box (the SVG origin fix) is supported everywhere since 2020. Everything else is baseline keyframes.