16 CSS Bounce Animations13 / 16

Pure CSSMIT licensed

Bouncing Letters Wave

Individual letters in a word bounce up in sequence, creating a wave motion — the classic 404-page / playful branding pattern. Deep pink surface with the letters of "BOUNCE" spelled out, each letter animated with staggered animation-delay. Every letter runs the SAME translateY bounce keyframe but starts at a different point in the cycle, creating the wave illusion. Users search for this when building error pages, empty states, or playful hero moments.

Published

Live Demo
Try it

The code

<div class="bn-13">
  <p class="bn-13__label">Bouncing letters wave</p>
  <p class="bn-13__word" aria-label="Bounce">
    <span aria-hidden="true">B</span>
    <span aria-hidden="true">O</span>
    <span aria-hidden="true">U</span>
    <span aria-hidden="true">N</span>
    <span aria-hidden="true">C</span>
    <span aria-hidden="true">E</span>
  </p>
  <p class="bn-13__hint">Each letter uses the same bounce keyframe with staggered <code>animation-delay</code>.</p>
</div>
.bn-13 {
  --page-bg: #831843;
  --ink: #fdf2f8;
  --sub: rgba(253,242,248,.7);
  --accent: #f9a8d4;
  --accent-hi: #f0abfc;
  font-family: -apple-system,BlinkMacSystemFont,'Poppins','Inter','Segoe UI',sans-serif;
  width: 100%;
  min-height: 100vh;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 32px;
  padding: 32px;
  background: radial-gradient(60% 60% at 50% 40%, rgba(240,171,252,.16) 0%, transparent 60%), linear-gradient(135deg,#831843 0%,#9d174d 50%,#701a75 100%);
  color: var(--ink);
  text-align: center;
}

.bn-13 *,
.bn-13 *::before,
.bn-13 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.bn-13 ::selection {
  background: var(--accent);
  color: #831843;
}

.bn-13__label {
  font-size: .9rem;
  font-weight: 600;
  color: var(--sub);
  letter-spacing: .14em;
  text-transform: uppercase;
}

.bn-13__word {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-family: 'Poppins','Inter',sans-serif;
  font-size: clamp(4rem,10vw,7rem);
  font-weight: 900;
  letter-spacing: -.03em;
  line-height: 1.2;
  color: var(--accent-hi);
}

.bn-13__word span {
  display: inline-block;
  animation: bn-13-letter 1.8s cubic-bezier(.55,0,.45,1) infinite;
  will-change: transform;
}

.bn-13__word span:nth-child(1) {
  animation-delay: 0s;
}

.bn-13__word span:nth-child(2) {
  animation-delay: .12s;
}

.bn-13__word span:nth-child(3) {
  animation-delay: .24s;
}

.bn-13__word span:nth-child(4) {
  animation-delay: .36s;
}

.bn-13__word span:nth-child(5) {
  animation-delay: .48s;
}

.bn-13__word span:nth-child(6) {
  animation-delay: .60s;
}

@keyframes bn-13-letter {
  0% {
    transform: translateY(0);
  }

  30% {
    transform: translateY(-20px);
  }

  60% {
    transform: translateY(-8px);
  }

  100% {
    transform: translateY(0);
  }
}

.bn-13__hint {
  font-size: .78rem;
  color: var(--sub);
  max-width: 420px;
  line-height: 1.5;
}

.bn-13__hint code {
  font-family: 'JetBrains Mono',ui-monospace,monospace;
  font-size: .72rem;
  padding: 1px 5px;
  border-radius: 4px;
  background: rgba(253,242,248,.14);
}

@media (prefers-reduced-motion: reduce) {
  .bn-13__word span {
    animation: none!important;
    transform: none!important;
  }
}
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 Bounce 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: Bouncing Letters Wave
Source: https://codefronts.com/motion/css-bounce-animation/bouncing-letters-wave/

Individual letters in a word bounce up in sequence, creating a wave motion — the classic 404-page / playful branding pattern. Deep pink surface with the letters of "BOUNCE" spelled out, each letter animated with staggered animation-delay. Every letter runs the SAME translateY bounce keyframe but starts at a different point in the cycle, creating the wave illusion. Users search for this when building error pages, empty states, or playful hero moments.
## HTML
```html
<div class="bn-13">
  <p class="bn-13__label">Bouncing letters wave</p>
  <p class="bn-13__word" aria-label="Bounce">
    <span aria-hidden="true">B</span>
    <span aria-hidden="true">O</span>
    <span aria-hidden="true">U</span>
    <span aria-hidden="true">N</span>
    <span aria-hidden="true">C</span>
    <span aria-hidden="true">E</span>
  </p>
  <p class="bn-13__hint">Each letter uses the same bounce keyframe with staggered <code>animation-delay</code>.</p>
</div>
```
## CSS
```css
.bn-13 {
  --page-bg: #831843;
  --ink: #fdf2f8;
  --sub: rgba(253,242,248,.7);
  --accent: #f9a8d4;
  --accent-hi: #f0abfc;
  font-family: -apple-system,BlinkMacSystemFont,'Poppins','Inter','Segoe UI',sans-serif;
  width: 100%;
  min-height: 100vh;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 32px;
  padding: 32px;
  background: radial-gradient(60% 60% at 50% 40%, rgba(240,171,252,.16) 0%, transparent 60%), linear-gradient(135deg,#831843 0%,#9d174d 50%,#701a75 100%);
  color: var(--ink);
  text-align: center;
}

.bn-13 *,
.bn-13 *::before,
.bn-13 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.bn-13 ::selection {
  background: var(--accent);
  color: #831843;
}

.bn-13__label {
  font-size: .9rem;
  font-weight: 600;
  color: var(--sub);
  letter-spacing: .14em;
  text-transform: uppercase;
}

.bn-13__word {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-family: 'Poppins','Inter',sans-serif;
  font-size: clamp(4rem,10vw,7rem);
  font-weight: 900;
  letter-spacing: -.03em;
  line-height: 1.2;
  color: var(--accent-hi);
}

.bn-13__word span {
  display: inline-block;
  animation: bn-13-letter 1.8s cubic-bezier(.55,0,.45,1) infinite;
  will-change: transform;
}

.bn-13__word span:nth-child(1) {
  animation-delay: 0s;
}

.bn-13__word span:nth-child(2) {
  animation-delay: .12s;
}

.bn-13__word span:nth-child(3) {
  animation-delay: .24s;
}

.bn-13__word span:nth-child(4) {
  animation-delay: .36s;
}

.bn-13__word span:nth-child(5) {
  animation-delay: .48s;
}

.bn-13__word span:nth-child(6) {
  animation-delay: .60s;
}

@keyframes bn-13-letter {
  0% {
    transform: translateY(0);
  }

  30% {
    transform: translateY(-20px);
  }

  60% {
    transform: translateY(-8px);
  }

  100% {
    transform: translateY(0);
  }
}

.bn-13__hint {
  font-size: .78rem;
  color: var(--sub);
  max-width: 420px;
  line-height: 1.5;
}

.bn-13__hint code {
  font-family: 'JetBrains Mono',ui-monospace,monospace;
  font-size: .72rem;
  padding: 1px 5px;
  border-radius: 4px;
  background: rgba(253,242,248,.14);
}

@media (prefers-reduced-motion: reduce) {
  .bn-13__word span {
    animation: none!important;
    transform: none!important;
  }
}
```

How this works

Each letter is a separate <span> inside a container. All spans share the SAME @keyframes bn-13-letter — a 3-stop bounce cascade: 0% translateY(0) → 30% translateY(-20px) → 60% translateY(-8px) → 100% translateY(0). The two-peak pattern (big up, small up, settle) mimics a decaying ball bounce.

The staggered animation-delay on each letter creates the wave — letter 1 starts at 0s, letter 2 at 0.12s, letter 3 at 0.24s, and so on. With a 1.8s cycle and 6 letters, the total stagger (0.6s) is exactly 1/3 of the cycle — so as one letter reaches its peak, the next is 40% into its rise, the third is 20%, creating the illusion of continuous flowing motion.

The stagger formula: total-stagger < cycle-duration. If the total stagger equals or exceeds the cycle duration, letters don't overlap in phases and the wave breaks into 6 separate blinks. For 6 letters + 1.8s cycle, 0.12s per letter (0.72s total) works — under the 1.8s cycle limit but visually spread enough.

Deep magenta background with the word "BOUNCE" in a large playful sans (Poppins / Fraunces / any display font). Each letter has a slight rotation offset for organic feel. On prefers-reduced-motion, the letters stay static — the word is still visible, just not animated.

Make it yours

  • Change bounce height by editing translateY(-20px) — 12px is subtle, 32px is playful. Match to your brand's motion vocabulary.
  • Change the stagger delay by editing the --i multiplier — 0.08s per letter for tight wave, 0.16s per letter for spread-out wave. Total stagger must stay under the cycle duration.
  • Change the word by adding/removing <span> elements — the CSS uses nth-child(n) for delays but you can switch to a CSS custom property + --i for arbitrary word lengths.
  • For colorful branding (each letter a different color), add --hue variable per letter and use background: hsl(var(--hue), 70%, 60%); -webkit-background-clip: text; color: transparent.
  • For Tailwind: use custom keyframes + animation-delay-[0.12s] arbitrary values on each letter. Requires tailwind.config.js keyframes registration. See FAQ #4.

Gotchas — read before shipping

  • Do NOT use inline-block on letters with animation — the bounce works fine but text baseline shifts as elements rise, which can push the surrounding text around. Use display: inline-block on letters AND on the container so the container reserves its full height.
  • For accessibility, wrap the visible letters in a <span aria-label="Bounce"> so screen readers hear the word as a whole, not letter-by-letter. Individual <span> letters can have aria-hidden="true".
  • The stagger delay MUST be less than the animation duration. If cycle is 1.8s and you use 0.4s per letter with 6 letters (2.4s total stagger), the last letter starts AFTER the first letter has finished its bounce — no wave motion, just sequential blinks.
  • For long words (10+ letters), reduce per-letter delay to keep total stagger under cycle duration. Or use a shorter cycle duration.
  • The vertical bounce compresses the container's line-height calculation. If the surrounding UI depends on precise text baseline (e.g., sitting on a line rule), the bouncing letters will break the alignment. Reserve fixed height on the container.
  • For platforms with LTR + RTL support, the animation-delay progression works the same in both directions — but if you're using directional wording ("forward," "onwards"), the wave may read differently in RTL contexts.
  • For prefers-reduced-motion, disable all bounces — the letters stay static. The word is still visible; only decorative wave motion is disabled.

Browser support

ChromeSafariFirefoxEdge
45+10+40+45+

transform + @keyframes + animation-delay. Universal, GPU-accelerated.

Techniques used in this demo

Search CodeFronts

Loading…