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.

Live Demo
Try it

Build UI like this from scratch: Flexbox, Grid, animations & responsive CSS

ZTM · The CSS Bootcamp · 30‑day money-back guarantee · Affiliate link

10% off at checkout with code
Check ZTM Course

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…