Letter Slide Up
Each letter rises from below with a regular stagger — pure CSS animation that gives depth to any heading.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Text has 56 effects, including 4 free. Explore the category →
3 usage examples



How it works
The slideUpLetter keyframe moves each letter from opacity: 0; transform: translateY(100%) to its natural state in 0.4 s, with animation-fill-mode: forwards holding each letter visible on arrival. Wrapping each character in a <span> set to display: inline-block is required: inline elements ignore CSS transforms.
The splitText(el, text, delay) function slices the text character by character and injects one <span> per letter into the target element. Each span gets an animation-delay of index × delay seconds (0.06 s by default). Spaces increment the counter without creating a span, producing a short pause in the cascade — visually, spaces appear to arrive too.
To replay: replaySlideUp() clears innerHTML, waits 50 ms for the browser to invalidate previous animation states, then calls splitText again. At initial load, spans are pre-built in the HTML with inline animation-delay attributes, triggering the animation on first render without any JS call.
Accessibility
- prefers-reduced-motion absent: no
@media (prefers-reduced-motion: reduce)block in the delivered CSS — the animation plays even when the OS requests reduced motion. Add that block to set letters directly toopacity: 1; transform: none. - Animated text is real text content, not a canvas or image — screen readers read the letters normally, no
aria-*attributes are needed. - The Replay button is a native
<button>, keyboard-focusable (Tab + Enter or Space). - Contrast: white text (
#fff) on background#0a0a0f, ratio ~21:1 — well above the WCAG AA threshold (4.5:1). - No
roleoraria-labelis defined by the effect: since the content is readable text, native semantic rendering is sufficient.
Browser compatibility
Requires CSS @keyframes and basic DOM manipulation (innerHTML, createElement) — supported in all modern browsers. Zero external dependencies.
If CSS animations are unsupported (very old browser), all letters stay at <code>opacity: 0</code> — the text is invisible but no JS errors are thrown.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="demo-preview">
<span class="demo-text text-slide-up" id="slideUpText">
<!-- splitText(el, "Your text", 0.06) generates these spans -->
<span style="animation-delay: 0s">Y</span>
<span style="animation-delay: .06s">o</span>
<!-- … one <span> per letter, spaces as plain text … -->
</span>
<button class="replay-btn" onclick="replaySlideUp()">Replay</button>
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| Stagger delay | 0.06 s per letter | 3rd argument of splitText(el, text, delay) — increase (0.10 s) to space out letters, decrease (0.03 s) for 15+ character text. |
| Animation duration | 0.4 s | Value 0.4s in the CSS rule .text-slide-up span — 0.6 s for smoother entry, 0.2 s for a quick flash. |
| Starting offset | translateY(100%) | Property transform: translateY(100%) in .text-slide-up span — reduce to 60% for a subtle touch; add overflow: hidden to the parent to clip the travel. |
| Easing | ease | Value ease in animation: .4s ease — swap for cubic-bezier(0.22, 1, 0.36, 1) for a gentle overshoot, or linear for a constant rise. |
| Font size | 2.5 rem | font-size in .demo-text — 3–6 rem for a full-screen hero, 1.5–2 rem for editorial text. |
| Font weight | 800 | font-weight in .demo-text — 900 for maximum impact, 400 for light editorial text. |
| Letter spacing | −0.02 em | letter-spacing in .demo-text — increase to 0.05–0.15 em for an airy display type look. |
| Button accent color | #818cf8 | CSS variable --primary-light in :root — replace with your brand color for the Replay button. |
FAQ
splitText is a shared utility across the Effect.Labs text effects. For standalone use, add it before the IIFE: function splitText(el,text,delay){el.innerHTML='';text.split('').forEach(function(ch,i){if(ch===' '){el.appendChild(document.createTextNode(' '));}else{var s=document.createElement('span');s.textContent=ch;s.style.animationDelay=(i*delay)+'s';el.appendChild(s);}});}.text-slide-up span applies to any element carrying that class. For multiple instances, write a factory: function makeSlideUp(id,text,delay){var el=document.getElementById(id);splitText(el,text,delay||0.06);return function(){el.innerHTML='';setTimeout(function(){splitText(el,text,delay||0.06);},50);}} Call var replay=makeSlideUp('myId','My title',0.06) per element..text-slide-up element empty in the HTML (no pre-built spans). Wire an IntersectionObserver to it: when it enters the viewport, call splitText(el, 'Your title', 0.06) — spans are injected and the CSS animation starts immediately, without modifying the CSS or keyframe.