Kinetic Typography
Five full-screen type blocks where words slide, stagger, and settle into place as each panel enters the viewport.
Kinetic Typography the 2nd of 12 designs in the 12 CSS Scroll Animations collection. The design pairs CSS styling with a small amount of JavaScript for interactivity. Copy the HTML, CSS and JavaScript panels below into your project — the JS is self-contained, has zero dependencies, and is safe to drop into any framework (React, Vue, Svelte, plain HTML). The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.
Live preview
This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.
The code
<!-- Block 1: Dark gold --> <section class="block b1"> <span class="counter-tag">01 / 05</span> <div class="ghost">DE</div> <div class="kl"><span class="w">DESIGN</span> <span class="w"><span class="em">that</span></span></div> <div class="kl"><span class="w">MOVES</span></div> <div class="kl"><span class="w">PEOPLE</span></div> <div class="rule"></div> <p class="sub">Scroll-driven motion. Cinematic reveals. Zero libraries.</p> </section> <!-- Block 2: Cream terracotta --> <section class="block b2"> <span class="counter-tag">02 / 05</span> <div class="ghost">GO</div> <div class="kl"><span class="w">EVERY</span></div> <div class="kl"><span class="w">WORD</span> <span class="w"><span class="em">earns</span></span></div> <div class="kl"><span class="w">ITS </span><span class="w">PLACE</span></div> <div class="rule"></div> <p class="sub">Typography as choreography. Each line its own entrance.</p> </section> <!-- Block 3: Dark purple --> <section class="block b3"> <span class="counter-tag">03 / 05</span> <div class="ghost">MO</div> <div class="kl"><span class="w">MOTION</span></div> <div class="solo"><span class="w"><span class="em">is the</span></span></div> <div class="kl" style="margin-top:0.1em"><span class="w">MESSAGE</span></div> <div class="rule"></div> <p class="sub">The way it arrives matters as much as what it says.</p> </section> <!-- Block 4: Warm cream --> <section class="block b4"> <span class="counter-tag">04 / 05</span> <div class="ghost">AR</div> <div class="kl"><span class="w">ARRIVE</span></div> <div class="kl"><span class="w">ON </span><span class="w"><span class="em">cue,</span></span></div> <div class="kl"><span class="w">ALWAYS</span></div> <div class="rule"></div> <p class="sub">Timing is everything. Precision in every millisecond.</p> </section> <!-- Block 5: Dark neon mono --> <section class="block b5"> <span class="counter-tag">05 / 05</span> <div class="ghost" style="-webkit-text-stroke:1px rgba(57,255,20,0.04)">SC</div> <div class="mono"><span class="w">SCROLL</span></div> <div class="mono"><span class="w">IS</span> <span class="w">THE</span></div> <div class="mono"><span class="w">INPUT</span></div> <div class="rule" style="background:#39FF14"></div> <p class="sub" style="color:rgba(57,255,20,0.35);font-family:'Courier New',monospace;font-style:normal;font-size:clamp(12px,1.2vw,16px);letter-spacing:0.08em">USER_SCROLL → ANIMATION_TRIGGER → REVEAL_STATE</p> </section>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{overflow-x:hidden}
.block{
min-height:100vh;
display:flex;flex-direction:column;justify-content:center;
padding:8rem 7vw;position:relative;overflow:hidden;
}
/* Block themes */
.b1{background:#0E0C09}
.b2{background:#EDE8DF}
.b3{background:#100A08}
.b4{background:#F0EDE6}
.b5{background:#080C10}
/* Kinetic lines */
.kl{
font-family:'Bebas Neue',sans-serif;
font-size:clamp(62px,12vw,180px);
line-height:0.91;
overflow:hidden;display:block;
}
.kl .w{
display:inline-block;
transform:translateY(115%);opacity:0;
transition:transform 0.95s cubic-bezier(0.16,1,0.3,1),opacity 0.38s ease;
}
.kl.in .w{transform:translateY(0);opacity:1}
.kl.in .w:nth-child(2){transition-delay:0.07s}
.kl.in .w:nth-child(3){transition-delay:0.14s}
.kl.in .w:nth-child(4){transition-delay:0.21s}
.kl.in .w:nth-child(5){transition-delay:0.28s}
/* per-block text colors */
.b1 .kl,.b3 .kl,.b5 .kl{color:#F0EBE0}
.b2 .kl,.b4 .kl{color:#16110C}
/* accent color per block */
.b1 .em{color:#C9A96E}
.b2 .em{color:#B34A38}
.b3 .em{color:#9B7FE8}
.b4 .em{color:#2E6B8A}
.b5 .em{color:#39FF14}
.em{font-family:'Fraunces',serif;font-style:italic}
/* rule */
.rule{
height:2px;width:0;opacity:0;margin:2.5rem 0 2rem;
transition:width 1s cubic-bezier(0.16,1,0.3,1) 0.45s,opacity 0.4s ease 0.45s;
}
.rule.in{width:80px;opacity:1}
.b1 .rule,.b3 .rule,.b5 .rule{background:#C9A96E}
.b2 .rule{background:#B34A38}
.b4 .rule{background:#2E6B8A}
.b5 .rule{background:#39FF14}
/* sub */
.sub{
font-family:'Fraunces',serif;font-style:italic;
font-size:clamp(14px,1.7vw,22px);
opacity:0;transform:translateY(14px);
transition:opacity 0.8s ease 0.7s,transform 0.8s cubic-bezier(0.16,1,0.3,1) 0.7s;
}
.sub.in{opacity:1;transform:translateY(0)}
.b1 .sub,.b3 .sub,.b5 .sub{color:rgba(240,235,224,0.35)}
.b2 .sub,.b4 .sub{color:rgba(22,17,12,0.38)}
/* ghost watermark */
.ghost{
position:absolute;user-select:none;pointer-events:none;
font-family:'Bebas Neue',sans-serif;font-size:28vw;line-height:1;
color:transparent;
}
.b1 .ghost,.b3 .ghost,.b5 .ghost{-webkit-text-stroke:1px rgba(255,255,255,0.03);bottom:-8%;right:-4%}
.b2 .ghost,.b4 .ghost{-webkit-text-stroke:1px rgba(0,0,0,0.04);bottom:-8%;right:-4%}
/* counter tag */
.counter-tag{
position:absolute;top:3rem;right:4rem;
font-family:'Fraunces',serif;font-style:italic;
font-size:13px;letter-spacing:0.05em;
}
.b1 .counter-tag,.b3 .counter-tag,.b5 .counter-tag{color:rgba(240,235,224,0.2)}
.b2 .counter-tag,.b4 .counter-tag{color:rgba(22,17,12,0.2)}
/* large italic solo line */
.solo{
font-family:'Fraunces',serif;font-style:italic;
font-size:clamp(40px,7vw,110px);
line-height:1;overflow:hidden;display:block;
}
.solo .w{
display:inline-block;
transform:translateY(115%);opacity:0;
transition:transform 1.1s cubic-bezier(0.16,1,0.3,1),opacity 0.5s ease;
}
.solo.in .w{transform:translateY(0);opacity:1}
.solo.in .w:nth-child(2){transition-delay:0.09s}
.solo.in .w:nth-child(3){transition-delay:0.18s}
.b5{font-family:monospace}
.mono{
font-family:'Courier New',monospace;
font-size:clamp(48px,9vw,140px);
font-weight:700;line-height:0.9;
color:#39FF14;overflow:hidden;display:block;
}
.mono .w{
display:inline-block;
transform:translateX(-40px);opacity:0;
transition:transform 0.8s cubic-bezier(0.16,1,0.3,1),opacity 0.4s ease;
}
.mono.in .w{transform:translateX(0);opacity:1}
.mono.in .w:nth-child(2){transition-delay:0.06s}
.mono.in .w:nth-child(3){transition-delay:0.12s}
.mono.in .w:nth-child(4){transition-delay:0.18s}
.mono.in .w:nth-child(5){transition-delay:0.24s} const io = new IntersectionObserver(entries=>{
entries.forEach(e=>{
if(!e.isIntersecting)return;
e.target.classList.add('in');
});
},{threshold:0.25});
document.querySelectorAll('.kl,.solo,.mono,.rule,.sub').forEach(el=>io.observe(el));