← all topics

Rolling Text

CSS

Each character is wrapped in an overflow-hidden slot. An inner column of two characters slides up on hover — the outgoing char exits upward while the incoming char rises from below. Staggered transition-delay creates a cascading wave across the word.

DDEESSIIGGNN
TTHHIINNKK
HHEELLLLOO
Controls
40ms
400ms

concepts

overflow clip + fixed height

Each character slot is display: inline-block with overflow: hidden and height: 1em. This creates a viewport window that shows exactly one character at a time — anything above or below is clipped.

two-character column

Inside each slot, a flex column holds the outgoing char on top and the incoming char below. On hover, the entire column translateY(-1em), sliding the outgoing char up out of view and the incoming char up into the slot.

staggered transition-delay

Each slot gets transition-delay: i * staggerMs. The first character starts moving immediately; later characters wait proportionally. This creates a cascading left-to-right (or right-to-left) wave across the word.

color transition

The outgoing character transitions its color toward the incoming character's accent color as it exits. The incoming character is always pre-colored — no transition needed. Both fire on the same delay, so the color shift is synchronized with the slide.

core pattern

// Each character slot:
// <span style="overflow:hidden; height:1em">
//   <span style="display:flex; flex-direction:column;
//                transform: translateY(-1em) /* hovered */
//                transition: transform 400ms ease 80ms /* i=2 */">
//     <span style="color: accent /* fades in */">D</span>
//     <span style="color: accent">C</span>
//   </span>
// </span>

// Stagger calculation:
const delay = charIndex * staggerMs;
// i=0 → 0ms, i=1 → 40ms, i=2 → 80ms …

// Reverse stagger:
const staggerIndex = reversed ? len - 1 - i : i;
const delay = staggerIndex * staggerMs;