22 CSS Transition Effects 21 / 22
Split Text Reveal Transition
Character-split hero headline, word-by-word reveal, rotating word swap, per-character colour wave and text scramble with progressive decode.
This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.
The code
<div class="page">
<h1>Split Text Reveal Transition</h1>
<p class="subtitle">Characters, words and lines animated into view</p>
<!-- 1. Hero -->
<section>
<h2>Hero Headline — Character Split</h2>
<button class="replay-btn" onclick="runAll()">↺ Replay All</button>
<div class="hero">
<div class="big split-text from-below" id="heroL1"><div class="line"></div></div>
<div class="big split-text from-below" id="heroL2"><div class="line"></div></div>
<div class="sub split-text from-below" id="heroSub"><div class="line"></div></div>
</div>
</section>
<!-- 2. Word reveal -->
<section>
<h2>Word-by-Word Reveal</h2>
<div class="word-reveal" id="wordReveal"></div>
</section>
<!-- 3. Rotating swap -->
<section>
<h2>Rotating Word Swap</h2>
<div class="swap-container">
<span class="swap-static">We build</span>
<div class="swap-word" id="swapWord"><span class="active">interfaces</span></div>
</div>
</section>
<!-- 4. Wave -->
<section>
<h2>Character Colour Wave</h2>
<div class="wave-text" id="waveText"></div>
</section>
<!-- 5. Scramble -->
<section>
<h2>Text Scramble Reveal</h2>
<div class="scramble-block" id="scrambleBlock"></div>
</section>
</div> <div class="page">
<h1>Split Text Reveal Transition</h1>
<p class="subtitle">Characters, words and lines animated into view</p>
<!-- 1. Hero -->
<section>
<h2>Hero Headline — Character Split</h2>
<button class="replay-btn" onclick="runAll()">↺ Replay All</button>
<div class="hero">
<div class="big split-text from-below" id="heroL1"><div class="line"></div></div>
<div class="big split-text from-below" id="heroL2"><div class="line"></div></div>
<div class="sub split-text from-below" id="heroSub"><div class="line"></div></div>
</div>
</section>
<!-- 2. Word reveal -->
<section>
<h2>Word-by-Word Reveal</h2>
<div class="word-reveal" id="wordReveal"></div>
</section>
<!-- 3. Rotating swap -->
<section>
<h2>Rotating Word Swap</h2>
<div class="swap-container">
<span class="swap-static">We build</span>
<div class="swap-word" id="swapWord"><span class="active">interfaces</span></div>
</div>
</section>
<!-- 4. Wave -->
<section>
<h2>Character Colour Wave</h2>
<div class="wave-text" id="waveText"></div>
</section>
<!-- 5. Scramble -->
<section>
<h2>Text Scramble Reveal</h2>
<div class="scramble-block" id="scrambleBlock"></div>
</section>
</div>* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Segoe UI', sans-serif; background: #0c0c0e; min-height: 100vh; overflow-x: hidden; }
.page { padding: 70px 40px; max-width: 1000px; margin: 0 auto; }
h1 { color: #fff; text-align: center; font-size: 2rem; margin-bottom: 8px; }
.subtitle { color: rgba(255,255,255,.4); text-align: center; margin-bottom: 60px; }
section { margin-bottom: 80px; }
section > h2 { color: rgba(255,255,255,.3); font-size: .72rem; text-transform: uppercase; letter-spacing: 3px; margin-bottom: 30px; }
.replay-btn {
display: inline-flex; align-items: center; gap: 8px; margin-bottom: 24px;
padding: 8px 18px; border-radius: 8px; border: 1px solid rgba(255,255,255,.15);
background: rgba(255,255,255,.05); color: rgba(255,255,255,.7); font-size: .82rem; cursor: pointer;
transition: background .2s;
}
.replay-btn:hover { background: rgba(255,255,255,.1); }
/* ─────────────────────────────────────
Shared split char helpers
Each .char is a clip container; the inner span slides in from below / above
───────────────────────────────────── */
.split-text { overflow: hidden; display: inline-block; }
.split-text .line { overflow: hidden; display: block; }
.split-text .char {
display: inline-block; overflow: hidden;
vertical-align: top; /* prevent layout shift */
}
.split-text .char .inner {
display: inline-block;
transition: transform .6s cubic-bezier(.16,1,.3,1), opacity .6s ease;
}
/* Initial hidden states */
.from-below .char .inner { transform: translateY(110%); opacity: 0; }
.from-above .char .inner { transform: translateY(-110%); opacity: 0; }
.from-left .char .inner { transform: translateX(-80px); opacity: 0; }
.from-right .char .inner { transform: translateX(80px); opacity: 0; }
.scale-blur .char .inner { transform: scale(1.4); opacity: 0; filter: blur(6px); transition: transform .7s cubic-bezier(.16,1,.3,1), opacity .6s, filter .6s; }
.rotate-in .char .inner { transform: rotateX(90deg) translateY(40px); opacity: 0; transform-origin: top center; transition: transform .65s cubic-bezier(.16,1,.3,1), opacity .5s; }
/* Revealed state */
.char.visible .inner { transform: none; opacity: 1; filter: none; }
/* ─────────────────────────────────────
1. Hero headline — from-below
───────────────────────────────────── */
.hero { text-align: center; padding: 20px 0; }
.hero .big { font-size: clamp(2.5rem, 7vw, 5rem); font-weight: 900; line-height: 1.05; letter-spacing: -.02em; }
.hero .line1 { color: #fff; }
.hero .line2 {
background: linear-gradient(90deg,#a78bfa,#60a5fa);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
}
.hero .sub {
margin-top: 20px; color: rgba(255,255,255,.5); font-size: 1.1rem; overflow: hidden;
}
/* ─────────────────────────────────────
2. Staggered word reveal
───────────────────────────────────── */
.word-reveal { font-size: 1.6rem; font-weight: 700; color: #fff; line-height: 1.5; }
.word-reveal .word { display: inline-block; overflow: hidden; margin-right: .25em; }
.word-reveal .word .inner {
display: inline-block;
transform: translateY(100%);
transition: transform .55s cubic-bezier(.16,1,.3,1);
}
.word-reveal .word.visible .inner { transform: none; }
/* ─────────────────────────────────────
3. Rotating word swap
───────────────────────────────────── */
.swap-container { display: flex; align-items: baseline; gap: 12px; font-size: 2rem; font-weight: 800; }
.swap-static { color: #fff; }
.swap-word {
color: #a78bfa; display: inline-block; overflow: hidden;
min-width: 200px;
}
.swap-word .active {
display: block; animation: swap-in .5s cubic-bezier(.16,1,.3,1) forwards;
}
.swap-word .exit {
display: block; animation: swap-out .4s ease-in forwards;
}
@keyframes swap-in { from { transform: translateY(100%); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes swap-out { from { transform: none; opacity: 1; } to { transform: translateY(-100%); opacity: 0; } }
/* ─────────────────────────────────────
4. Per-character colour wave
───────────────────────────────────── */
.wave-text {
font-size: 3rem; font-weight: 900; letter-spacing: -.02em;
display: flex; flex-wrap: wrap; gap: 0;
}
.wave-text .char {
display: inline-block; overflow: hidden;
color: rgba(255,255,255,.15);
transition: color .4s ease;
}
.wave-text .char.lit { color: #fff; }
.wave-text .char.space { width: .35em; }
/* ─────────────────────────────────────
5. Scramble + reveal (JS)
───────────────────────────────────── */
.scramble-block { display: flex; flex-direction: column; gap: 10px; }
.scramble-item { overflow: hidden; }
.scramble-target { font-size: 1.2rem; font-weight: 600; color: #fff; font-family: monospace; letter-spacing: .02em; }
.scramble-target .reveal-bar {
position: relative; display: inline-block;
background: linear-gradient(90deg,#7c3aed,#3b82f6); border-radius: 2px;
height: 3px; width: 0; display: block; margin-top: 4px;
transition: width 1s ease;
}
.scramble-target.done .reveal-bar { width: 100%; } * { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Segoe UI', sans-serif; background: #0c0c0e; min-height: 100vh; overflow-x: hidden; }
.page { padding: 70px 40px; max-width: 1000px; margin: 0 auto; }
h1 { color: #fff; text-align: center; font-size: 2rem; margin-bottom: 8px; }
.subtitle { color: rgba(255,255,255,.4); text-align: center; margin-bottom: 60px; }
section { margin-bottom: 80px; }
section > h2 { color: rgba(255,255,255,.3); font-size: .72rem; text-transform: uppercase; letter-spacing: 3px; margin-bottom: 30px; }
.replay-btn {
display: inline-flex; align-items: center; gap: 8px; margin-bottom: 24px;
padding: 8px 18px; border-radius: 8px; border: 1px solid rgba(255,255,255,.15);
background: rgba(255,255,255,.05); color: rgba(255,255,255,.7); font-size: .82rem; cursor: pointer;
transition: background .2s;
}
.replay-btn:hover { background: rgba(255,255,255,.1); }
/* ─────────────────────────────────────
Shared split char helpers
Each .char is a clip container; the inner span slides in from below / above
───────────────────────────────────── */
.split-text { overflow: hidden; display: inline-block; }
.split-text .line { overflow: hidden; display: block; }
.split-text .char {
display: inline-block; overflow: hidden;
vertical-align: top; /* prevent layout shift */
}
.split-text .char .inner {
display: inline-block;
transition: transform .6s cubic-bezier(.16,1,.3,1), opacity .6s ease;
}
/* Initial hidden states */
.from-below .char .inner { transform: translateY(110%); opacity: 0; }
.from-above .char .inner { transform: translateY(-110%); opacity: 0; }
.from-left .char .inner { transform: translateX(-80px); opacity: 0; }
.from-right .char .inner { transform: translateX(80px); opacity: 0; }
.scale-blur .char .inner { transform: scale(1.4); opacity: 0; filter: blur(6px); transition: transform .7s cubic-bezier(.16,1,.3,1), opacity .6s, filter .6s; }
.rotate-in .char .inner { transform: rotateX(90deg) translateY(40px); opacity: 0; transform-origin: top center; transition: transform .65s cubic-bezier(.16,1,.3,1), opacity .5s; }
/* Revealed state */
.char.visible .inner { transform: none; opacity: 1; filter: none; }
/* ─────────────────────────────────────
1. Hero headline — from-below
───────────────────────────────────── */
.hero { text-align: center; padding: 20px 0; }
.hero .big { font-size: clamp(2.5rem, 7vw, 5rem); font-weight: 900; line-height: 1.05; letter-spacing: -.02em; }
.hero .line1 { color: #fff; }
.hero .line2 {
background: linear-gradient(90deg,#a78bfa,#60a5fa);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
}
.hero .sub {
margin-top: 20px; color: rgba(255,255,255,.5); font-size: 1.1rem; overflow: hidden;
}
/* ─────────────────────────────────────
2. Staggered word reveal
───────────────────────────────────── */
.word-reveal { font-size: 1.6rem; font-weight: 700; color: #fff; line-height: 1.5; }
.word-reveal .word { display: inline-block; overflow: hidden; margin-right: .25em; }
.word-reveal .word .inner {
display: inline-block;
transform: translateY(100%);
transition: transform .55s cubic-bezier(.16,1,.3,1);
}
.word-reveal .word.visible .inner { transform: none; }
/* ─────────────────────────────────────
3. Rotating word swap
───────────────────────────────────── */
.swap-container { display: flex; align-items: baseline; gap: 12px; font-size: 2rem; font-weight: 800; }
.swap-static { color: #fff; }
.swap-word {
color: #a78bfa; display: inline-block; overflow: hidden;
min-width: 200px;
}
.swap-word .active {
display: block; animation: swap-in .5s cubic-bezier(.16,1,.3,1) forwards;
}
.swap-word .exit {
display: block; animation: swap-out .4s ease-in forwards;
}
@keyframes swap-in { from { transform: translateY(100%); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes swap-out { from { transform: none; opacity: 1; } to { transform: translateY(-100%); opacity: 0; } }
/* ─────────────────────────────────────
4. Per-character colour wave
───────────────────────────────────── */
.wave-text {
font-size: 3rem; font-weight: 900; letter-spacing: -.02em;
display: flex; flex-wrap: wrap; gap: 0;
}
.wave-text .char {
display: inline-block; overflow: hidden;
color: rgba(255,255,255,.15);
transition: color .4s ease;
}
.wave-text .char.lit { color: #fff; }
.wave-text .char.space { width: .35em; }
/* ─────────────────────────────────────
5. Scramble + reveal (JS)
───────────────────────────────────── */
.scramble-block { display: flex; flex-direction: column; gap: 10px; }
.scramble-item { overflow: hidden; }
.scramble-target { font-size: 1.2rem; font-weight: 600; color: #fff; font-family: monospace; letter-spacing: .02em; }
.scramble-target .reveal-bar {
position: relative; display: inline-block;
background: linear-gradient(90deg,#7c3aed,#3b82f6); border-radius: 2px;
height: 3px; width: 0; display: block; margin-top: 4px;
transition: width 1s ease;
}
.scramble-target.done .reveal-bar { width: 100%; }/* ── 1. Build char splits ─────────────────────────── */
function buildCharSplit(el, text, baseDelay = 0) {
const line = el.querySelector('.line');
line.innerHTML = '';
[...text].forEach((ch, i) => {
const wrap = document.createElement('span');
wrap.className = 'char';
if (ch === ' ') { wrap.style.width = '.3em'; wrap.style.display = 'inline-block'; }
const inner = document.createElement('span');
inner.className = 'inner';
inner.textContent = ch;
wrap.appendChild(inner);
line.appendChild(wrap);
// stagger
setTimeout(() => wrap.classList.add('visible'), baseDelay + i * 40);
});
}
function resetSplit(el) {
el.querySelectorAll('.char').forEach(c => c.classList.remove('visible'));
}
/* ── 2. Word reveal ───────────────────────────────── */
const WORDS = 'Design systems that feel like magic.'.split(' ');
function buildWordReveal() {
const el = document.getElementById('wordReveal');
el.innerHTML = '';
WORDS.forEach((w, i) => {
const wrap = document.createElement('span');
wrap.className = 'word';
const inner = document.createElement('span');
inner.className = 'inner';
inner.textContent = w;
wrap.appendChild(inner);
el.appendChild(wrap);
el.appendChild(document.createTextNode(' '));
setTimeout(() => wrap.classList.add('visible'), 300 + i * 90);
});
}
/* ── 3. Rotating swap ────────────────────────────── */
const SWAP_WORDS = ['interfaces','experiences','products','identities','systems','futures'];
let swapIdx = 0;
function nextSwap() {
const el = document.getElementById('swapWord');
const active = el.querySelector('.active');
swapIdx = (swapIdx + 1) % SWAP_WORDS.length;
if (active) {
active.className = 'exit';
setTimeout(() => active.remove(), 420);
}
const next = document.createElement('span');
next.className = 'active';
next.textContent = SWAP_WORDS[swapIdx];
el.appendChild(next);
}
setInterval(nextSwap, 2200);
/* ── 4. Wave ─────────────────────────────────────── */
const WAVE_TEXT = 'CREATIVE MOTION';
function buildWave() {
const el = document.getElementById('waveText');
el.innerHTML = '';
[...WAVE_TEXT].forEach((ch, i) => {
const span = document.createElement('span');
span.className = 'char' + (ch === ' ' ? ' space' : '');
span.textContent = ch;
el.appendChild(span);
});
}
function runWave(delay = 0) {
const chars = [...document.querySelectorAll('#waveText .char:not(.space)')];
chars.forEach(c => c.classList.remove('lit'));
chars.forEach((c, i) => setTimeout(() => c.classList.add('lit'), delay + i * 55));
// Loop
setTimeout(() => {
chars.forEach(c => c.classList.remove('lit'));
setTimeout(() => runWave(0), 400);
}, delay + chars.length * 55 + 800);
}
/* ── 5. Scramble ──────────────────────────────────── */
const LINES = ['The quick brown fox','Jumps over the lazy','Dog at dawn'];
const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%';
function scramble(el, target, delay = 0) {
const bar = el.querySelector('.reveal-bar');
const text = el.querySelector('.text-content') || el;
let frame = 0;
const total = 30;
setTimeout(() => {
const iv = setInterval(() => {
const progress = frame / total;
const revealedCount = Math.floor(progress * target.length);
let out = '';
for (let i = 0; i < target.length; i++) {
if (target[i] === ' ') { out += ' '; continue; }
if (i < revealedCount) { out += target[i]; }
else { out += CHARS[Math.floor(Math.random() * CHARS.length)]; }
}
el.childNodes[0].textContent = out;
frame++;
if (frame > total) {
clearInterval(iv);
el.childNodes[0].textContent = target;
el.classList.add('done');
}
}, 35);
}, delay);
}
function buildScramble() {
const block = document.getElementById('scrambleBlock');
block.innerHTML = '';
LINES.forEach((line, i) => {
const item = document.createElement('div');
item.className = 'scramble-item';
const tgt = document.createElement('div');
tgt.className = 'scramble-target';
tgt.appendChild(document.createTextNode(line));
const bar = document.createElement('span');
bar.className = 'reveal-bar';
tgt.appendChild(bar);
item.appendChild(tgt);
block.appendChild(item);
scramble(tgt, line, 500 + i * 600);
});
}
/* ── Init & replay ───────────────────────────────── */
function heroLine1() {
const el = document.getElementById('heroL1');
el.classList.add('line1');
buildCharSplit(el, 'Build Beautiful', 0);
}
function heroLine2() {
const el = document.getElementById('heroL2');
el.classList.add('line2');
buildCharSplit(el, 'Transitions', 200);
}
function heroSub() {
buildCharSplit(document.getElementById('heroSub'), 'Every character, perfectly timed.', 600);
}
function runAll() {
heroLine1(); heroLine2(); heroSub();
buildWordReveal();
buildScramble();
}
buildWave();
runAll();
runWave(1200); /* ── 1. Build char splits ─────────────────────────── */
function buildCharSplit(el, text, baseDelay = 0) {
const line = el.querySelector('.line');
line.innerHTML = '';
[...text].forEach((ch, i) => {
const wrap = document.createElement('span');
wrap.className = 'char';
if (ch === ' ') { wrap.style.width = '.3em'; wrap.style.display = 'inline-block'; }
const inner = document.createElement('span');
inner.className = 'inner';
inner.textContent = ch;
wrap.appendChild(inner);
line.appendChild(wrap);
// stagger
setTimeout(() => wrap.classList.add('visible'), baseDelay + i * 40);
});
}
function resetSplit(el) {
el.querySelectorAll('.char').forEach(c => c.classList.remove('visible'));
}
/* ── 2. Word reveal ───────────────────────────────── */
const WORDS = 'Design systems that feel like magic.'.split(' ');
function buildWordReveal() {
const el = document.getElementById('wordReveal');
el.innerHTML = '';
WORDS.forEach((w, i) => {
const wrap = document.createElement('span');
wrap.className = 'word';
const inner = document.createElement('span');
inner.className = 'inner';
inner.textContent = w;
wrap.appendChild(inner);
el.appendChild(wrap);
el.appendChild(document.createTextNode(' '));
setTimeout(() => wrap.classList.add('visible'), 300 + i * 90);
});
}
/* ── 3. Rotating swap ────────────────────────────── */
const SWAP_WORDS = ['interfaces','experiences','products','identities','systems','futures'];
let swapIdx = 0;
function nextSwap() {
const el = document.getElementById('swapWord');
const active = el.querySelector('.active');
swapIdx = (swapIdx + 1) % SWAP_WORDS.length;
if (active) {
active.className = 'exit';
setTimeout(() => active.remove(), 420);
}
const next = document.createElement('span');
next.className = 'active';
next.textContent = SWAP_WORDS[swapIdx];
el.appendChild(next);
}
setInterval(nextSwap, 2200);
/* ── 4. Wave ─────────────────────────────────────── */
const WAVE_TEXT = 'CREATIVE MOTION';
function buildWave() {
const el = document.getElementById('waveText');
el.innerHTML = '';
[...WAVE_TEXT].forEach((ch, i) => {
const span = document.createElement('span');
span.className = 'char' + (ch === ' ' ? ' space' : '');
span.textContent = ch;
el.appendChild(span);
});
}
function runWave(delay = 0) {
const chars = [...document.querySelectorAll('#waveText .char:not(.space)')];
chars.forEach(c => c.classList.remove('lit'));
chars.forEach((c, i) => setTimeout(() => c.classList.add('lit'), delay + i * 55));
// Loop
setTimeout(() => {
chars.forEach(c => c.classList.remove('lit'));
setTimeout(() => runWave(0), 400);
}, delay + chars.length * 55 + 800);
}
/* ── 5. Scramble ──────────────────────────────────── */
const LINES = ['The quick brown fox','Jumps over the lazy','Dog at dawn'];
const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%';
function scramble(el, target, delay = 0) {
const bar = el.querySelector('.reveal-bar');
const text = el.querySelector('.text-content') || el;
let frame = 0;
const total = 30;
setTimeout(() => {
const iv = setInterval(() => {
const progress = frame / total;
const revealedCount = Math.floor(progress * target.length);
let out = '';
for (let i = 0; i < target.length; i++) {
if (target[i] === ' ') { out += ' '; continue; }
if (i < revealedCount) { out += target[i]; }
else { out += CHARS[Math.floor(Math.random() * CHARS.length)]; }
}
el.childNodes[0].textContent = out;
frame++;
if (frame > total) {
clearInterval(iv);
el.childNodes[0].textContent = target;
el.classList.add('done');
}
}, 35);
}, delay);
}
function buildScramble() {
const block = document.getElementById('scrambleBlock');
block.innerHTML = '';
LINES.forEach((line, i) => {
const item = document.createElement('div');
item.className = 'scramble-item';
const tgt = document.createElement('div');
tgt.className = 'scramble-target';
tgt.appendChild(document.createTextNode(line));
const bar = document.createElement('span');
bar.className = 'reveal-bar';
tgt.appendChild(bar);
item.appendChild(tgt);
block.appendChild(item);
scramble(tgt, line, 500 + i * 600);
});
}
/* ── Init & replay ───────────────────────────────── */
function heroLine1() {
const el = document.getElementById('heroL1');
el.classList.add('line1');
buildCharSplit(el, 'Build Beautiful', 0);
}
function heroLine2() {
const el = document.getElementById('heroL2');
el.classList.add('line2');
buildCharSplit(el, 'Transitions', 200);
}
function heroSub() {
buildCharSplit(document.getElementById('heroSub'), 'Every character, perfectly timed.', 600);
}
function runAll() {
heroLine1(); heroLine2(); heroSub();
buildWordReveal();
buildScramble();
}
buildWave();
runAll();
runWave(1200);More from 22 CSS Transition Effects
Text Reveal AnimationImage Zoom Hover TransitionBackground Color TransitionBorder Animation TransitionNavigation Hover TransitionUnderline Animation HoverGlassmorphism Hover TransitionSlide-In Animation on ScrollFade In Fade Out TransitionLoading Skeleton TransitionModal Open Close TransitionPage Transition Effect
View the full collection →