16 CSS Fade In Animation Designs16 / 16

CSS + JSMIT licensed

Cascade Letter Drop Fade

JavaScript splits a headline character-by-character into spans with staggered animation-delay; each letter drops in from above with a spring bounce, creating a raindrop cascade.

Published

Live Demo
Try it

The code

<div class="fi-16" id="fi-16-root">
  <div>
    <div class="fi-16__label">Letter cascade</div>
    <span class="fi-16__title" id="fi-16-title">Cascade Letters</span>
    <p class="fi-16__sub" id="fi-16-sub" style="animation-delay:1.4s">JavaScript splits every character into a span with a staggered animation-delay, creating a raindrop-style letter cascade.</p>
    <div class="fi-16__row" id="fi-16-row" style="animation-delay:1.7s">
      <span class="fi-16__chip">JS split</span>
      <span class="fi-16__chip">nth-child delay</span>
      <span class="fi-16__chip">spring bounce</span>
    </div>
    <div class="fi-16__replay" id="fi-16-replay" style="animation-delay:1.9s">↺ replay animation</div>
  </div>
</div>
.fi-16 {
  --bg: #090b14;
  --gold: #f59e0b;
  --amber: #fbbf24;
  --text: #fffbeb;
  font-family: 'DM Sans',sans-serif;
  min-height: 340px;
  border-radius: 20px;
  display: grid;
  place-items: center;
  padding: 40px;
  overflow: hidden;
  text-align: center;
}

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

.fi-16 ::selection {
  background: var(--gold);
  color: #000;
}

.fi-16__label {
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: rgba(255,251,235,.35);
  margin-bottom: 20px;
}

.fi-16__title {
  font-family: 'Syne',sans-serif;
  font-size: clamp(1.8rem,4vw,3rem);
  font-weight: 800;
  color: var(--text);
  line-height: 1.2;
  margin-bottom: 16px;
  display: block;
}
/* JS injects letter spans */

.fi-16__letter {
  display: inline-block;
  opacity: 0;
  transform: translateY(-20px) rotate(-8deg);
  animation: fi-16-letter-drop .5s cubic-bezier(.34,1.56,.64,1) forwards;
}

.fi-16__letter.fi-16--space {
  margin-right: .35em;
}

.fi-16__sub {
  font-size: .95rem;
  color: rgba(255,251,235,.45);
  line-height: 1.6;
  max-width: 400px;
  margin-bottom: 28px;
  opacity: 0;
  animation: fi-16-fade-up .6s ease forwards;
}

.fi-16__row {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
  opacity: 0;
  animation: fi-16-fade-up .5s ease forwards;
}

.fi-16__chip {
  padding: 7px 16px;
  border-radius: 20px;
  font-size: .75rem;
  font-weight: 700;
  background: rgba(245,158,11,.12);
  border: 1px solid rgba(245,158,11,.25);
  color: var(--gold);
  transition: background .2s,transform .2s;
  cursor: default;
}

.fi-16__chip:hover {
  background: rgba(245,158,11,.22);
  transform: translateY(-2px);
}

.fi-16__replay {
  margin-top: 18px;
  display: inline-block;
  font-size: .72rem;
  font-weight: 600;
  color: rgba(255,251,235,.35);
  cursor: pointer;
  border-bottom: 1px dashed rgba(255,255,255,.15);
  padding-bottom: 2px;
  transition: color .2s;
  opacity: 0;
  animation: fi-16-fade-up .5s ease forwards;
}

.fi-16__replay:hover {
  color: var(--gold);
}

@keyframes fi-16-letter-drop {
  to {
    opacity: 1;
    transform: translateY(0) rotate(0deg);
  }
}

@keyframes fi-16-fade-up {
  to {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .fi-16 * {
    animation: none!important;
    opacity: 1!important;
    transform: none!important;
  }
}
(function(){
  const titleEl = document.getElementById('fi-16-title');
  const replay  = document.getElementById('fi-16-replay');
  const STEP = 0.05;

  function animate(){
    const text = 'Cascade Letters';
    titleEl.innerHTML = '';
    [...text].forEach((ch, i)=>{
      const span = document.createElement('span');
      span.className = 'fi-16__letter' + (ch === ' ' ? ' fi-16--space' : '');
      span.textContent = ch === ' ' ? '\u00a0' : ch;
      span.style.animationDelay = (0.1 + i * STEP)+'s';
      titleEl.appendChild(span);
    });
  }

  animate();

  replay.addEventListener('click',()=>{
    titleEl.querySelectorAll('.fi-16__letter').forEach(s=>{
      s.style.animation='none';void s.offsetWidth;
      s.style.animation='fi-16-letter-drop .5s cubic-bezier(.34,1.56,.64,1) forwards';
    });
  });
})();
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 Fade In Animation Design 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: Cascade Letter Drop Fade
Source: https://codefronts.com/motion/css-fade-in-animation/cascade-letter-drop-fade/

JavaScript splits a headline character-by-character into spans with staggered animation-delay; each letter drops in from above with a spring bounce, creating a raindrop cascade.
## HTML
```html
<div class="fi-16" id="fi-16-root">
  <div>
    <div class="fi-16__label">Letter cascade</div>
    <span class="fi-16__title" id="fi-16-title">Cascade Letters</span>
    <p class="fi-16__sub" id="fi-16-sub" style="animation-delay:1.4s">JavaScript splits every character into a span with a staggered animation-delay, creating a raindrop-style letter cascade.</p>
    <div class="fi-16__row" id="fi-16-row" style="animation-delay:1.7s">
      <span class="fi-16__chip">JS split</span>
      <span class="fi-16__chip">nth-child delay</span>
      <span class="fi-16__chip">spring bounce</span>
    </div>
    <div class="fi-16__replay" id="fi-16-replay" style="animation-delay:1.9s">↺ replay animation</div>
  </div>
</div>
```
## CSS
```css
.fi-16 {
  --bg: #090b14;
  --gold: #f59e0b;
  --amber: #fbbf24;
  --text: #fffbeb;
  font-family: 'DM Sans',sans-serif;
  min-height: 340px;
  border-radius: 20px;
  display: grid;
  place-items: center;
  padding: 40px;
  overflow: hidden;
  text-align: center;
}

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

.fi-16 ::selection {
  background: var(--gold);
  color: #000;
}

.fi-16__label {
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: rgba(255,251,235,.35);
  margin-bottom: 20px;
}

.fi-16__title {
  font-family: 'Syne',sans-serif;
  font-size: clamp(1.8rem,4vw,3rem);
  font-weight: 800;
  color: var(--text);
  line-height: 1.2;
  margin-bottom: 16px;
  display: block;
}
/* JS injects letter spans */

.fi-16__letter {
  display: inline-block;
  opacity: 0;
  transform: translateY(-20px) rotate(-8deg);
  animation: fi-16-letter-drop .5s cubic-bezier(.34,1.56,.64,1) forwards;
}

.fi-16__letter.fi-16--space {
  margin-right: .35em;
}

.fi-16__sub {
  font-size: .95rem;
  color: rgba(255,251,235,.45);
  line-height: 1.6;
  max-width: 400px;
  margin-bottom: 28px;
  opacity: 0;
  animation: fi-16-fade-up .6s ease forwards;
}

.fi-16__row {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
  opacity: 0;
  animation: fi-16-fade-up .5s ease forwards;
}

.fi-16__chip {
  padding: 7px 16px;
  border-radius: 20px;
  font-size: .75rem;
  font-weight: 700;
  background: rgba(245,158,11,.12);
  border: 1px solid rgba(245,158,11,.25);
  color: var(--gold);
  transition: background .2s,transform .2s;
  cursor: default;
}

.fi-16__chip:hover {
  background: rgba(245,158,11,.22);
  transform: translateY(-2px);
}

.fi-16__replay {
  margin-top: 18px;
  display: inline-block;
  font-size: .72rem;
  font-weight: 600;
  color: rgba(255,251,235,.35);
  cursor: pointer;
  border-bottom: 1px dashed rgba(255,255,255,.15);
  padding-bottom: 2px;
  transition: color .2s;
  opacity: 0;
  animation: fi-16-fade-up .5s ease forwards;
}

.fi-16__replay:hover {
  color: var(--gold);
}

@keyframes fi-16-letter-drop {
  to {
    opacity: 1;
    transform: translateY(0) rotate(0deg);
  }
}

@keyframes fi-16-fade-up {
  to {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .fi-16 * {
    animation: none!important;
    opacity: 1!important;
    transform: none!important;
  }
}
```

## JavaScript
```js
(function(){
  const titleEl = document.getElementById('fi-16-title');
  const replay  = document.getElementById('fi-16-replay');
  const STEP = 0.05;

  function animate(){
    const text = 'Cascade Letters';
    titleEl.innerHTML = '';
    [...text].forEach((ch, i)=>{
      const span = document.createElement('span');
      span.className = 'fi-16__letter' + (ch === ' ' ? ' fi-16--space' : '');
      span.textContent = ch === ' ' ? '\u00a0' : ch;
      span.style.animationDelay = (0.1 + i * STEP)+'s';
      titleEl.appendChild(span);
    });
  }

  animate();

  replay.addEventListener('click',()=>{
    titleEl.querySelectorAll('.fi-16__letter').forEach(s=>{
      s.style.animation='none';void s.offsetWidth;
      s.style.animation='fi-16-letter-drop .5s cubic-bezier(.34,1.56,.64,1) forwards';
    });
  });
})();
```

How this works

The JavaScript function iterates over each character in the headline string using [...text].forEach (spread to correctly handle multi-byte chars). Each character becomes a span.fi-16__letter with animation-delay: 0.1 + index × 0.05 + 's'. The keyframe starts each letter at opacity: 0; transform: translateY(-20px) rotate(-8deg) — tipped and airborne — and settles to upright via cubic-bezier(.34, 1.56, .64, 1) spring overshoot.

Spaces receive a .fi-16--space class with a margin-right: .35em so word gaps are preserved. The replay handler iterates all letter spans, nullifies their animation with 'none', forces a reflow via offsetWidth access to flush the browser's style cache, then restores the animation string — this is the canonical pure-JS animation restart technique without cloning nodes.

Make it yours

  • Reduce letter delay STEP from 0.05 to 0.03s for faster cascade — good for longer headlines.
  • Change the keyframe to translateY(20px → 0) (upward from below) for letters rising rather than dropping.
  • Reduce initial rotate(-8deg) to -3deg for a subtler tilt on each letter.
  • Apply different colors to vowels vs consonants via post-split CSS class assignment in the JS function.

Gotchas — read before shipping

  • Spreading a string with [...text] correctly handles emoji and multi-byte Unicode — text.split('') would split emoji into surrogate pairs, corrupting the characters.
  • If the headline text changes dynamically, call the animate() function again — the old spans are replaced automatically since innerHTML is cleared first.
  • The animation: none → reflow → restore technique for replay fires a synchronous layout via offsetWidth. In tight loops, batch reflows by reading all offsetWidth values first, then writing all animation resets.

Browser support

ChromeSafariFirefoxEdge
60+12+60+60+

Spread operator [...text] for Unicode-safe split requires ES6; works in all evergreen browsers

Techniques used in this demo

Search CodeFronts

Loading…