25 CSS Text Animations 09 / 25
CSS Fade Up Stagger Text Animation
Words cascade upward one after another with staggered opacity and translateY transitions — the signature micro-animation of modern landing pages.
The code
<div class="ta-09">
<div class="ta-09__stage">
<p class="ta-09__label ta-09__item" style="--d:0">The future of</p>
<h2 class="ta-09__headline">
<span class="ta-09__item" style="--d:1">Design</span>
<span class="ta-09__item ta-09__accent" style="--d:2">is</span>
<span class="ta-09__item" style="--d:3">Motion.</span>
</h2>
<p class="ta-09__body ta-09__item" style="--d:4">Staggered entrances create perceived momentum and guide the eye through content hierarchies with purpose.</p>
</div>
</div> <div class="ta-09">
<div class="ta-09__stage">
<p class="ta-09__label ta-09__item" style="--d:0">The future of</p>
<h2 class="ta-09__headline">
<span class="ta-09__item" style="--d:1">Design</span>
<span class="ta-09__item ta-09__accent" style="--d:2">is</span>
<span class="ta-09__item" style="--d:3">Motion.</span>
</h2>
<p class="ta-09__body ta-09__item" style="--d:4">Staggered entrances create perceived momentum and guide the eye through content hierarchies with purpose.</p>
</div>
</div>.ta-09, .ta-09 *, .ta-09 *::before, .ta-09 *::after {
margin: 0; padding: 0; box-sizing: border-box;
}
.ta-09 ::selection { background: #6d28d9; color: #fff; }
.ta-09 {
--bg: #f8f7ff;
min-height: 100vh;
background: var(--bg);
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
font-family: 'Plus Jakarta Sans', 'Segoe UI', sans-serif;
}
.ta-09__stage {
max-width: 480px;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.ta-09__item {
opacity: 0;
transform: translateY(24px);
animation: ta-09-fadeup 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
animation-delay: calc(var(--d) * 0.12s);
}
.ta-09__label {
font-size: 0.78rem;
letter-spacing: 0.15em;
text-transform: uppercase;
color: #7c3aed;
}
.ta-09__headline {
font-size: clamp(2rem, 5.5vw, 3.2rem);
font-weight: 800;
color: #1e1b4b;
line-height: 1.1;
display: flex;
gap: 0.3em;
flex-wrap: wrap;
}
.ta-09__accent {
color: #7c3aed;
font-style: italic;
font-weight: 400;
}
.ta-09__body {
font-size: 0.88rem;
line-height: 1.65;
color: #64748b;
max-width: 380px;
}
@keyframes ta-09-fadeup {
to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
.ta-09__item { animation: none; opacity: 1; transform: none; }
} .ta-09, .ta-09 *, .ta-09 *::before, .ta-09 *::after {
margin: 0; padding: 0; box-sizing: border-box;
}
.ta-09 ::selection { background: #6d28d9; color: #fff; }
.ta-09 {
--bg: #f8f7ff;
min-height: 100vh;
background: var(--bg);
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
font-family: 'Plus Jakarta Sans', 'Segoe UI', sans-serif;
}
.ta-09__stage {
max-width: 480px;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.ta-09__item {
opacity: 0;
transform: translateY(24px);
animation: ta-09-fadeup 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
animation-delay: calc(var(--d) * 0.12s);
}
.ta-09__label {
font-size: 0.78rem;
letter-spacing: 0.15em;
text-transform: uppercase;
color: #7c3aed;
}
.ta-09__headline {
font-size: clamp(2rem, 5.5vw, 3.2rem);
font-weight: 800;
color: #1e1b4b;
line-height: 1.1;
display: flex;
gap: 0.3em;
flex-wrap: wrap;
}
.ta-09__accent {
color: #7c3aed;
font-style: italic;
font-weight: 400;
}
.ta-09__body {
font-size: 0.88rem;
line-height: 1.65;
color: #64748b;
max-width: 380px;
}
@keyframes ta-09-fadeup {
to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
.ta-09__item { animation: none; opacity: 1; transform: none; }
}How this works
Each word or phrase is a separate element starting with opacity: 0 and transform: translateY(24px). The ta-09-fadeup keyframe animates both to their final values simultaneously, but each element carries a progressively larger animation-delay — staggered so each word begins its reveal after the previous one has already partially risen.
The animation-fill-mode: both declaration is critical: it applies the 0% keyframe state (opacity: 0, translated down) before the animation starts, preventing a flash of visible content during the delay period. The easing uses a custom bezier curve that accelerates quickly and decelerates sharply, giving the words a sense of momentum settling into their resting position.
Customize
- Reduce the translateY offset from
24pxto10pxfor a subtle entrance, or increase to50pxfor a dramatic rise from below. - Change the stagger gap between words by adjusting the delay increments —
0.08sapart creates tight cascade,0.25sapart creates a slow reveal. - Add
transform: scale(0.95)to the start keyframe alongside translateY for a subtle grow-in effect that adds dimension. - Trigger on scroll by using an
IntersectionObserverthat adds an.animateclass to the container, then scope the animation to.animate .ta-09__word. - Apply to a grid of cards rather than inline text by giving each card the same stagger pattern — it transforms a static grid into a sequenced content reveal.
Watch out for
animation-fill-mode: bothis essential — without it, elements are visible at full opacity during the delay phase before snapping to the start keyframe state.- Do not animate
marginorpaddingalongsidetranslateY— they cause layout reflow on every frame. Always usetransformfor movement. - Applying this animation to dozens of elements simultaneously creates a thundering herd of repaints; batch elements into groups of 5–7 with shared timing for smoother performance.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 36+ | 9+ | 16+ | 36+ |
animation-fill-mode: both is universally supported. translateY animations are GPU-composited in all modern browsers.