Slide Reveal
Reveals text from below using an overflow:hidden container and a translateY transition — pure CSS, zero JS, cascade-ready for multi-line and multi-word use.
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 mechanism relies on two complementary CSS rules. The .text-reveal-container applies overflow: hidden, turning the container into a mask: anything that overflows downward is invisible. The .text-reveal span starts at transform: translateY(100%) — fully positioned below the container's bottom edge, hidden at the initial state.
The @keyframes revealUp animation defines only a final step: to { transform: translateY(0) }. The browser automatically interpolates from the translateY(100%) declared on .text-reveal. The shorthand animation: 1s ease 0s 1 normal forwards running revealUp sets the duration (1 s), easing (ease), delay (0 s), iteration count (1), and fill-mode (forwards), which freezes the text in its final position after the rise.
The rule .demo-text span { display: inline-block } prepares word-by-word or character-by-character variations. By splitting the text into individual <span> elements — manually or via JS — each fragment inherits the same mask mechanism and can receive an increasing animation-delay for a synchronized cascade.
The effect is 100% CSS with no embedded JavaScript. Replaying it after load takes two lines: remove the .text-reveal class, force a reflow with el.offsetWidth, then add it back — the browser restarts the animation from translateY(100%).
Accessibility
- prefers-reduced-motion: absent from the code — no
@media (prefers-reduced-motion: reduce)rule is included. Recommended: add@media (prefers-reduced-motion: reduce) { .text-reveal { animation: none; transform: none; } }to display text immediately without motion. - Text stays in the DOM flow and is natively readable by screen readers regardless of its visual position during the animation.
- The animation moves no interactive element and creates no focus trap — keyboard navigation is not affected.
- The effect defines no text color of its own: contrast ratio depends entirely on the integration context and must be verified per WCAG AA (≥ 4.5:1).
- No
aria-labelorrolein the provided markup — if the text is decorative, addaria-hidden="true"; if informative, leave it in the flow.
Browser compatibility
Requires transform, @keyframes, and animation — supported in all modern browsers since 2013. Zero external dependencies.
Without CSS animation support (browsers before 2013), text stays locked at <code>translateY(100%)</code> and is not visible. Add <code>@supports not (animation: none) { .text-reveal { transform: none; } }</code> to secure very old environments.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="text-reveal-container">
<span class="text-reveal demo-text">Your title here</span>
</div>
<!-- Cascade: repeat the block with increasing animation-delay via CSS -->
<div class="text-reveal-container">
<span class="text-reveal demo-text">First line</span>
</div>
<div class="text-reveal-container">
<span class="text-reveal demo-text">Second line</span>
</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 |
|---|---|---|
| animation-duration in .text-reveal | 1s | Rise duration. 0.5 s = sharp and modern, 1.2 s = cinematic and slow. |
| animation-timing-function in .text-reveal | ease | Speed curve. cubic-bezier(0.16,1,0.3,1) = expressive spring, ease-out = smooth deceleration, linear = constant speed. |
| animation-delay in .text-reveal | 0s | Delay before start. Increasing values (0 s, 0.12 s, 0.24 s…) on sibling spans create a cascade with no extra JS. |
| transform: translateY(…) in .text-reveal | 100% | Starting offset. 110–120% adds a visible gap below the container and accentuates the mask on large fonts. |
| font-size in .demo-text | 2.5rem | Text size. From 1 rem (labels) to 8 rem (super-headlines) — no internal constraint. |
| letter-spacing in .demo-text | -0.02em | Character spacing. Negative = condensed (large headings), 0 em to 0.05 em = airy (labels and subtitles). |
FAQ
<span> elements (manually or via text.split(' ').map(…) in JS), wrap each in a .text-reveal-container, and apply increasing animation-delay values (0 s, 0.12 s, 0.24 s…) to each .text-reveal. The mask mechanism works identically for each fragment..text-reveal class (or set animation: none inline), (2) force a synchronous reflow with element.offsetWidth, (3) add the class back. The browser restarts from translateY(100%) and relaunches the transition.useState and a reset useEffect; in Vue, use :class; in Svelte, use class:. The CSS mechanism is identical regardless of framework.