Word by Word
Words emerge one after another with a fade and a gentle rise — dramatic reveal built on a single CSS animation and vanilla JS.
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 effect relies on a single CSS animation, wordAppear, that transitions each word from opacity:0; transform:translateY(20px) to its final state in 0.6 s using an ease curve and fill-mode:forwards. Each word lives in a <span> with an inline animation-delay computed by the JS function splitWords: the first word at 0 s, then +0.3 s per additional word.
The function splitWords(el, text, delay) splits the string on whitespace, empties the target element, creates one <span> per word with its computed animation-delay (i * delay + 's'), and inserts the nodes into the DOM. As soon as they are inserted, the CSS rule .text-word-by-word span takes over and fires the animations in cascade — no further JS is needed for the initial render.
The Replay button clears the element (innerHTML = ''), waits 50 ms for the browser to flush computed styles and reset the animation cycle, then calls initWordByWord() again. The delay is essential: removing and immediately reinserting spans without a pause prevents the browser from restarting the @keyframes.
Code note: in the original version, replayWordByWord and splitWords are defined inside the IIFE and not exposed globally — the onclick button will not work without adaptation (see FAQ). The initial animation, however, works in pure CSS as soon as the <span> elements are in the DOM.
Accessibility
- prefers-reduced-motion absent: the effect does not check the system preference — all words animate even if the user has requested reduced motion. To cover this case, add:
@media (prefers-reduced-motion: reduce) { .text-word-by-word span { animation: none; opacity: 1; transform: none; } }. - Each word's text is in the DOM in plain text — screen readers read the full sentence without waiting for the animation to finish.
- The Replay button is a native
<button>element, keyboard-focusable (Tab · Enter · Space) with no extra configuration. - White text on a dark background (#0a0a0f) provides a contrast ratio above 10:1 — compliant with WCAG AA and AAA.
- No
aria-*attributes orroleare set on the container: addaria-labelif the animated text is the only accessible content in the section.
Browser compatibility
Requires CSS3 @keyframes and animation-fill-mode — available in all modern browsers since 2013. Zero external dependencies, no canvas required.
If CSS animations are disabled (high-contrast mode on Windows, old browser), all spans default to <code>opacity:0</code> — text disappears. To guarantee readability without animation, add <code>@media (prefers-reduced-motion: reduce) { .text-word-by-word span { opacity: 1; transform: none; animation: none; } }</code>.
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-word-by-word" id="wordByWordText">
<span style="animation-delay: 0s">WORD</span>
<span style="animation-delay: 0.3s">BY</span>
<span style="animation-delay: 0.6s">WORD</span>
</span>
<button class="replay-btn" onclick="replayWordByWord()">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 |
|---|---|---|
| delay (splitWords arg) | 0.3 | Seconds between consecutive words appearing. 0.5 s = dramatic pace; 0.1 s = rapid cascade. |
| animation-duration (.text-word-by-word span) | 0.6s | Entry duration per word. 0.9 s = very soft fade; 0.25 s = sharp snap. |
| translateY initial (.text-word-by-word span) | 20px | Start height before the rise. 0 px = pure fade with no movement; 40 px = dramatic entry from below. |
| animation-timing-function | ease | Animation curve. ease-out for a natural deceleration; cubic-bezier(0.22,1,0.36,1) for an elastic overshoot. |
| font-size (.demo-text) | 2.5rem | Text size. 4–6 rem for a full-screen hero; 1.5 rem for a subtitle. |
| font-weight (.demo-text) | 800 | Font weight. 700 = standard bold; 900 = black for a very assertive typographic style. |
| letter-spacing (.demo-text) | -0.02em | Letter spacing. Positive (0.1 em) for a spaced all-caps look; more negative (−0.05 em) for a very tight headline. |
FAQ
'WORD BY WORD' string passed to splitWords() inside initWordByWord(): the function automatically splits into words and computes the animation-delay values. You can also edit the <span> elements directly in the HTML, adjusting delays in 0.3 s increments.replayWordByWord is defined inside the IIFE and is not globally accessible. Expose it before closing the IIFE: window.replayWordByWord = replayWordByWord;. Also make sure splitWords is defined in the global scope or inside the IIFE before it is called..text-word-by-word container a distinct id and call splitWords(el, text, delay) on each one. Every instance is self-contained — it only touches its own element and its own <span> nodes.