30 CSS Shake Animations11 / 30

CSS + JSMIT licensed

Shake Button CSS Keyframes

The keyframe cookbook: four buttons carrying the four base shakes (horizontal, vertical, rotate, chaos) with their frame tables on the card, and a tunable specimen where amplitude and speed are CSS custom properties you flip live — one @keyframes rule, infinite variants.

Published

Live Demo
Try it

The code

<div class="shk-11-group">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400..700&family=IBM+Plex+Mono:wght@500;600&display=swap" rel="stylesheet">
<section class="shk-11a" aria-label="Four base shake keyframes on four buttons">
  <div class="shk-11a__grid">
    <div class="shk-11a__cell"><button class="shk-11a__btn shk-11a__btn--x" type="button">Shake X</button><code class="shk-11a__code">translate: ±8px 0</code></div>
    <div class="shk-11a__cell"><button class="shk-11a__btn shk-11a__btn--y" type="button">Shake Y</button><code class="shk-11a__code">translate: 0 ±6px</code></div>
    <div class="shk-11a__cell"><button class="shk-11a__btn shk-11a__btn--r" type="button">Rotate</button><code class="shk-11a__code">rotate: ±4deg</code></div>
    <div class="shk-11a__cell"><button class="shk-11a__btn shk-11a__btn--c" type="button">Chaos</button><code class="shk-11a__code">all three, phase-shifted</code></div>
  </div>
  <p class="shk-11a__hint" aria-hidden="true">hover each — every button shake ever shipped is one of these four</p>
</section>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400..700&family=IBM+Plex+Mono:wght@500;600&display=swap" rel="stylesheet">
<section class="shk-11b" aria-label="Shake with live-tunable amplitude and speed">
  <div class="shk-11b__panel" id="shk-11b-panel" style="--amp:6px;--speed:.5s">
    <div class="shk-11b__group" role="group" aria-label="Amplitude"><span class="shk-11b__lab">--amp</span><button class="shk-11b__chip" type="button" data-prop="--amp" data-val="3px">3px</button><button class="shk-11b__chip is-on" type="button" data-prop="--amp" data-val="6px">6px</button><button class="shk-11b__chip" type="button" data-prop="--amp" data-val="12px">12px</button></div>
    <button class="shk-11b__btn" id="shk-11b-btn" type="button">Run the shake</button>
    <div class="shk-11b__group" role="group" aria-label="Speed"><span class="shk-11b__lab">--speed</span><button class="shk-11b__chip" type="button" data-prop="--speed" data-val=".3s">.3s</button><button class="shk-11b__chip is-on" type="button" data-prop="--speed" data-val=".5s">.5s</button><button class="shk-11b__chip" type="button" data-prop="--speed" data-val=".9s">.9s</button></div>
    <code class="shk-11b__read" id="shk-11b-read">animation: shake .5s · translate peaks at 6px</code>
  </div>
</section>
</div>
.shk-11-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  width: 100%;
}

.shk-11-group > section {
  flex: 1 1 480px;
  min-width: min(100%,360px);
}

.shk-11a,
.shk-11a *,
.shk-11a *::before,
.shk-11a *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.shk-11a {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-content: center;
  gap: 26px;
  padding: 48px 24px;
  background: #f2efe8;
  font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
}

.shk-11a__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit,minmax(170px,1fr));
  gap: 18px;
  width: min(760px,90vw);
}

.shk-11a__cell {
  display: grid;
  gap: 12px;
  justify-items: center;
  padding: 28px 18px;
  background: #fff;
  border: 2px solid #17150f;
  border-radius: 16px;
  box-shadow: 5px 5px 0 #17150f;
}

.shk-11a__btn {
  padding: 14px 26px;
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  border: 2px solid #17150f;
  border-radius: 12px;
  cursor: pointer;
  transition: scale .12s;
}

.shk-11a__btn:active {
  scale: .95;
}

.shk-11a__btn--x {
  background: oklch(0.85 0.16 85);
  color: #17150f;
}

.shk-11a__btn--y {
  background: oklch(0.8 0.14 200);
  color: #17150f;
}

.shk-11a__btn--r {
  background: oklch(0.78 0.16 340);
  color: #17150f;
}

.shk-11a__btn--c {
  background: #17150f;
  color: #f2efe8;
}

.shk-11a__btn--x:hover {
  animation: shk-11a-x .5s cubic-bezier(.36,.07,.19,.97);
}

.shk-11a__btn--y:hover {
  animation: shk-11a-y .5s cubic-bezier(.36,.07,.19,.97);
}

.shk-11a__btn--r:hover {
  animation: shk-11a-r .55s ease-in-out;
}

.shk-11a__btn--c:hover {
  animation: shk-11a-c .55s cubic-bezier(.36,.07,.19,.97);
}

.shk-11a__code {
  font-family: 'IBM Plex Mono',ui-monospace,monospace;
  font-size: 11px;
  color: rgba(23,21,15,.6);
  background: rgba(23,21,15,.06);
  padding: 4px 9px;
  border-radius: 6px;
}

.shk-11a__hint {
  text-align: center;
  font-size: 12px;
  color: rgba(23,21,15,.5);
}

@keyframes shk-11a-x {
  12% {
    translate: -8px 0;
  }

  30% {
    translate: 7px 0;
  }

  48% {
    translate: -5px 0;
  }

  66% {
    translate: 3px 0;
  }

  84% {
    translate: -1px 0;
  }
}

@keyframes shk-11a-y {
  12% {
    translate: 0 -6px;
  }

  30% {
    translate: 0 5px;
  }

  48% {
    translate: 0 -4px;
  }

  66% {
    translate: 0 2px;
  }

  84% {
    translate: 0 -1px;
  }
}

@keyframes shk-11a-r {
  15% {
    rotate: -4deg;
  }

  35% {
    rotate: 3.5deg;
  }

  55% {
    rotate: -2.5deg;
  }

  75% {
    rotate: 1.5deg;
  }

  90% {
    rotate: -.5deg;
  }
}

@keyframes shk-11a-c {
  12% {
    translate: -6px 2px;
    rotate: -1.2deg;
  }

  30% {
    translate: 5px -2px;
    rotate: 1deg;
  }

  48% {
    translate: -4px 1px;
    rotate: -.7deg;
  }

  66% {
    translate: 2px -1px;
    rotate: .4deg;
  }

  84% {
    translate: -1px 0;
    rotate: 0deg;
  }
}

@media (prefers-reduced-motion: reduce) {
  .shk-11a__btn:hover {
    animation: none!important;
  }
}

.shk-11b,
.shk-11b *,
.shk-11b *::before,
.shk-11b *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.shk-11b {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: linear-gradient(180deg,#101322,#0a0c16);
  font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
}

.shk-11b__panel {
  display: grid;
  gap: 22px;
  justify-items: center;
  padding: 40px 36px;
  border-radius: 22px;
  background: rgba(255,255,255,.045);
  border: 1px solid rgba(150,175,235,.16);
}

.shk-11b__group {
  display: flex;
  align-items: center;
  gap: 8px;
}

.shk-11b__lab {
  font-family: 'IBM Plex Mono',ui-monospace,monospace;
  font-size: 12px;
  color: oklch(0.78 0.12 250);
  margin-right: 4px;
}

.shk-11b__chip {
  padding: 8px 14px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: rgba(225,232,250,.7);
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(150,175,235,.2);
  border-radius: 999px;
  cursor: pointer;
  transition: background .15s,color .15s,border-color .15s;
}

.shk-11b__chip:hover {
  background: rgba(255,255,255,.1);
}

.shk-11b__chip.is-on {
  background: oklch(0.78 0.12 250 / .18);
  border-color: oklch(0.78 0.12 250);
  color: #eaf0ff;
}

.shk-11b__btn {
  padding: 18px 42px;
  font: inherit;
  font-size: 17px;
  font-weight: 700;
  color: #0a0c16;
  background: linear-gradient(180deg,#e8efff,#bcd0f5);
  border: 0;
  border-radius: 14px;
  cursor: pointer;
  transition: transform .15s;
}

.shk-11b__btn:active {
  transform: scale(.97);
}

.shk-11b__btn.is-shaking {
  animation: shk-11b-shake var(--speed,.5s) cubic-bezier(.36,.07,.19,.97);
}

.shk-11b__read {
  font-family: 'IBM Plex Mono',ui-monospace,monospace;
  font-size: 11.5px;
  color: rgba(225,232,250,.45);
}

@keyframes shk-11b-shake {
  12% {
    translate: calc(var(--amp) * -1) 0;
  }

  30% {
    translate: calc(var(--amp) * .9) 0;
  }

  48% {
    translate: calc(var(--amp) * -.65) 0;
  }

  66% {
    translate: calc(var(--amp) * .4) 0;
  }

  84% {
    translate: calc(var(--amp) * -.15) 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .shk-11b__btn.is-shaking {
    animation: none;
  }
}
/* No JavaScript — four @keyframes, one per base motion. The chaos variant phase-shifts X, Y and rotation so no two axes peak on the same frame; that desync is what makes it read as chaos. */

(function(){
  var panel=document.getElementById('shk-11b-panel'); if(!panel) return;
  var btn=document.getElementById('shk-11b-btn'),read=document.getElementById('shk-11b-read');
  btn.addEventListener('animationend',function(){ btn.classList.remove('is-shaking'); });
  function play(){ btn.classList.remove('is-shaking'); requestAnimationFrame(function(){ requestAnimationFrame(function(){ btn.classList.add('is-shaking'); }); }); }
  panel.addEventListener('click',function(e){
    var chip=e.target.closest('.shk-11b__chip');
    if(chip){
      panel.style.setProperty(chip.dataset.prop,chip.dataset.val);
      chip.parentElement.querySelectorAll('.shk-11b__chip').forEach(function(c){ c.classList.toggle('is-on',c===chip); });
      read.textContent='animation: shake '+getComputedStyle(panel).getPropertyValue('--speed').trim()+' · translate peaks at '+getComputedStyle(panel).getPropertyValue('--amp').trim();
      play(); return;
    }
    if(e.target===btn) play();
  });
})();
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
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: Shake Button CSS Keyframes
Source: https://codefronts.com/motion/css-shake-animation/shake-button-css-keyframes/

The keyframe cookbook: four buttons carrying the four base shakes (horizontal, vertical, rotate, chaos) with their frame tables on the card, and a tunable specimen where amplitude and speed are CSS custom properties you flip live — one @keyframes rule, infinite variants.
## HTML
```html
<div class="shk-11-group">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400..700&family=IBM+Plex+Mono:wght@500;600&display=swap" rel="stylesheet">
<section class="shk-11a" aria-label="Four base shake keyframes on four buttons">
  <div class="shk-11a__grid">
    <div class="shk-11a__cell"><button class="shk-11a__btn shk-11a__btn--x" type="button">Shake X</button><code class="shk-11a__code">translate: ±8px 0</code></div>
    <div class="shk-11a__cell"><button class="shk-11a__btn shk-11a__btn--y" type="button">Shake Y</button><code class="shk-11a__code">translate: 0 ±6px</code></div>
    <div class="shk-11a__cell"><button class="shk-11a__btn shk-11a__btn--r" type="button">Rotate</button><code class="shk-11a__code">rotate: ±4deg</code></div>
    <div class="shk-11a__cell"><button class="shk-11a__btn shk-11a__btn--c" type="button">Chaos</button><code class="shk-11a__code">all three, phase-shifted</code></div>
  </div>
  <p class="shk-11a__hint" aria-hidden="true">hover each — every button shake ever shipped is one of these four</p>
</section>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400..700&family=IBM+Plex+Mono:wght@500;600&display=swap" rel="stylesheet">
<section class="shk-11b" aria-label="Shake with live-tunable amplitude and speed">
  <div class="shk-11b__panel" id="shk-11b-panel" style="--amp:6px;--speed:.5s">
    <div class="shk-11b__group" role="group" aria-label="Amplitude"><span class="shk-11b__lab">--amp</span><button class="shk-11b__chip" type="button" data-prop="--amp" data-val="3px">3px</button><button class="shk-11b__chip is-on" type="button" data-prop="--amp" data-val="6px">6px</button><button class="shk-11b__chip" type="button" data-prop="--amp" data-val="12px">12px</button></div>
    <button class="shk-11b__btn" id="shk-11b-btn" type="button">Run the shake</button>
    <div class="shk-11b__group" role="group" aria-label="Speed"><span class="shk-11b__lab">--speed</span><button class="shk-11b__chip" type="button" data-prop="--speed" data-val=".3s">.3s</button><button class="shk-11b__chip is-on" type="button" data-prop="--speed" data-val=".5s">.5s</button><button class="shk-11b__chip" type="button" data-prop="--speed" data-val=".9s">.9s</button></div>
    <code class="shk-11b__read" id="shk-11b-read">animation: shake .5s · translate peaks at 6px</code>
  </div>
</section>
</div>
```
## CSS
```css
.shk-11-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  width: 100%;
}

.shk-11-group > section {
  flex: 1 1 480px;
  min-width: min(100%,360px);
}

.shk-11a,
.shk-11a *,
.shk-11a *::before,
.shk-11a *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.shk-11a {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-content: center;
  gap: 26px;
  padding: 48px 24px;
  background: #f2efe8;
  font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
}

.shk-11a__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit,minmax(170px,1fr));
  gap: 18px;
  width: min(760px,90vw);
}

.shk-11a__cell {
  display: grid;
  gap: 12px;
  justify-items: center;
  padding: 28px 18px;
  background: #fff;
  border: 2px solid #17150f;
  border-radius: 16px;
  box-shadow: 5px 5px 0 #17150f;
}

.shk-11a__btn {
  padding: 14px 26px;
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  border: 2px solid #17150f;
  border-radius: 12px;
  cursor: pointer;
  transition: scale .12s;
}

.shk-11a__btn:active {
  scale: .95;
}

.shk-11a__btn--x {
  background: oklch(0.85 0.16 85);
  color: #17150f;
}

.shk-11a__btn--y {
  background: oklch(0.8 0.14 200);
  color: #17150f;
}

.shk-11a__btn--r {
  background: oklch(0.78 0.16 340);
  color: #17150f;
}

.shk-11a__btn--c {
  background: #17150f;
  color: #f2efe8;
}

.shk-11a__btn--x:hover {
  animation: shk-11a-x .5s cubic-bezier(.36,.07,.19,.97);
}

.shk-11a__btn--y:hover {
  animation: shk-11a-y .5s cubic-bezier(.36,.07,.19,.97);
}

.shk-11a__btn--r:hover {
  animation: shk-11a-r .55s ease-in-out;
}

.shk-11a__btn--c:hover {
  animation: shk-11a-c .55s cubic-bezier(.36,.07,.19,.97);
}

.shk-11a__code {
  font-family: 'IBM Plex Mono',ui-monospace,monospace;
  font-size: 11px;
  color: rgba(23,21,15,.6);
  background: rgba(23,21,15,.06);
  padding: 4px 9px;
  border-radius: 6px;
}

.shk-11a__hint {
  text-align: center;
  font-size: 12px;
  color: rgba(23,21,15,.5);
}

@keyframes shk-11a-x {
  12% {
    translate: -8px 0;
  }

  30% {
    translate: 7px 0;
  }

  48% {
    translate: -5px 0;
  }

  66% {
    translate: 3px 0;
  }

  84% {
    translate: -1px 0;
  }
}

@keyframes shk-11a-y {
  12% {
    translate: 0 -6px;
  }

  30% {
    translate: 0 5px;
  }

  48% {
    translate: 0 -4px;
  }

  66% {
    translate: 0 2px;
  }

  84% {
    translate: 0 -1px;
  }
}

@keyframes shk-11a-r {
  15% {
    rotate: -4deg;
  }

  35% {
    rotate: 3.5deg;
  }

  55% {
    rotate: -2.5deg;
  }

  75% {
    rotate: 1.5deg;
  }

  90% {
    rotate: -.5deg;
  }
}

@keyframes shk-11a-c {
  12% {
    translate: -6px 2px;
    rotate: -1.2deg;
  }

  30% {
    translate: 5px -2px;
    rotate: 1deg;
  }

  48% {
    translate: -4px 1px;
    rotate: -.7deg;
  }

  66% {
    translate: 2px -1px;
    rotate: .4deg;
  }

  84% {
    translate: -1px 0;
    rotate: 0deg;
  }
}

@media (prefers-reduced-motion: reduce) {
  .shk-11a__btn:hover {
    animation: none!important;
  }
}

.shk-11b,
.shk-11b *,
.shk-11b *::before,
.shk-11b *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.shk-11b {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: linear-gradient(180deg,#101322,#0a0c16);
  font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
}

.shk-11b__panel {
  display: grid;
  gap: 22px;
  justify-items: center;
  padding: 40px 36px;
  border-radius: 22px;
  background: rgba(255,255,255,.045);
  border: 1px solid rgba(150,175,235,.16);
}

.shk-11b__group {
  display: flex;
  align-items: center;
  gap: 8px;
}

.shk-11b__lab {
  font-family: 'IBM Plex Mono',ui-monospace,monospace;
  font-size: 12px;
  color: oklch(0.78 0.12 250);
  margin-right: 4px;
}

.shk-11b__chip {
  padding: 8px 14px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: rgba(225,232,250,.7);
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(150,175,235,.2);
  border-radius: 999px;
  cursor: pointer;
  transition: background .15s,color .15s,border-color .15s;
}

.shk-11b__chip:hover {
  background: rgba(255,255,255,.1);
}

.shk-11b__chip.is-on {
  background: oklch(0.78 0.12 250 / .18);
  border-color: oklch(0.78 0.12 250);
  color: #eaf0ff;
}

.shk-11b__btn {
  padding: 18px 42px;
  font: inherit;
  font-size: 17px;
  font-weight: 700;
  color: #0a0c16;
  background: linear-gradient(180deg,#e8efff,#bcd0f5);
  border: 0;
  border-radius: 14px;
  cursor: pointer;
  transition: transform .15s;
}

.shk-11b__btn:active {
  transform: scale(.97);
}

.shk-11b__btn.is-shaking {
  animation: shk-11b-shake var(--speed,.5s) cubic-bezier(.36,.07,.19,.97);
}

.shk-11b__read {
  font-family: 'IBM Plex Mono',ui-monospace,monospace;
  font-size: 11.5px;
  color: rgba(225,232,250,.45);
}

@keyframes shk-11b-shake {
  12% {
    translate: calc(var(--amp) * -1) 0;
  }

  30% {
    translate: calc(var(--amp) * .9) 0;
  }

  48% {
    translate: calc(var(--amp) * -.65) 0;
  }

  66% {
    translate: calc(var(--amp) * .4) 0;
  }

  84% {
    translate: calc(var(--amp) * -.15) 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .shk-11b__btn.is-shaking {
    animation: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — four @keyframes, one per base motion. The chaos variant phase-shifts X, Y and rotation so no two axes peak on the same frame; that desync is what makes it read as chaos. */

(function(){
  var panel=document.getElementById('shk-11b-panel'); if(!panel) return;
  var btn=document.getElementById('shk-11b-btn'),read=document.getElementById('shk-11b-read');
  btn.addEventListener('animationend',function(){ btn.classList.remove('is-shaking'); });
  function play(){ btn.classList.remove('is-shaking'); requestAnimationFrame(function(){ requestAnimationFrame(function(){ btn.classList.add('is-shaking'); }); }); }
  panel.addEventListener('click',function(e){
    var chip=e.target.closest('.shk-11b__chip');
    if(chip){
      panel.style.setProperty(chip.dataset.prop,chip.dataset.val);
      chip.parentElement.querySelectorAll('.shk-11b__chip').forEach(function(c){ c.classList.toggle('is-on',c===chip); });
      read.textContent='animation: shake '+getComputedStyle(panel).getPropertyValue('--speed').trim()+' · translate peaks at '+getComputedStyle(panel).getPropertyValue('--amp').trim();
      play(); return;
    }
    if(e.target===btn) play();
  });
})();
```

How this works

Every button shake is one of four base motions — X, Y, rotation, or a compound 'chaos' mixing all three. Write them once, then stop hard-coding pixels. Parameterize with custom properties:

@keyframes shake{
  12%{translate:calc(var(--amp) * -1) 0}
  30%{translate:calc(var(--amp) * .9) 0}
  48%{translate:calc(var(--amp) * -.65) 0}
  66%{translate:calc(var(--amp) * .4) 0}
  84%{translate:calc(var(--amp) * -.15) 0}
}
.btn{--amp:6px; animation:shake var(--speed,.5s) both}

Now --amp and --speed are your design tokens: set them per component, per severity, even per theme — and the motion system stays a single source of truth. That's the difference between a snippet and a system.

Make it yours

  • Expose --amp as three tokens (--shake-sm:3px, --shake-md:6px, --shake-lg:12px) and reference severity, not pixels.
  • The chaos mix: keep rotation ≈ amplitude÷12 in degrees (6px → 0.5°) so the compound stays coherent.
  • Set --speed from a motion scale (fast 300ms / base 500ms / slow 800ms) shared with your other transitions.
  • In design systems, ship the keyframes in a @layer so product code can override tokens without duplicating frames.

Gotchas — read before shipping

  • Custom properties don't animate BETWEEN keyframes by default — here they're read once at animation start, which is exactly what you want; change mid-flight requires a restart.
  • calc(var(--amp) * -1) needs the space around '*' — calc parsing is whitespace-sensitive with vars.
  • Keyframes defined inside @media blocks are hoisted globally — name-collision across breakpoints silently overrides.
  • A translate shake on a button inside a flex row never affects siblings (transforms don't reflow) — that's WHY we never animate margin/left.

Browser support

ChromeSafariFirefoxEdge
111+15.4+113+111+

Floor set by oklch() as this demo is written. The core technique itself goes back further — Chrome all modern.

calc() with custom properties inside keyframes is baseline in all evergreen browsers. Individual translate/rotate properties: Chrome 104 / Safari 14.1 / Firefox 72.

Techniques used in this demo

Search CodeFronts

Loading…