Staggered Word Reveal
Meng ToCommunityCreated by Meng To
- Skill
- Tool integration
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a robust implementation for creating staggered, editorial-style text reveal animations. By splitting text into individual word spans and using the IntersectionObserver API, it ensures that headlines and short copy fade and rise into view gracefully as the user scrolls. The implementation prioritizes accessibility and performance by maintaining original text for screen readers, ensuring content remains visible if JavaScript fails, and respecting user reduced-motion preferences.
What it does
- Splits text content into individual word spans while preserving original accessibility labels.
- Triggers a staggered fade-in and slide-up animation using CSS transitions.
- Uses IntersectionObserver to trigger animations only when elements enter the viewport.
- Ensures content is readable and visible even if JavaScript is disabled.
- Includes built-in support for user-defined reduced-motion preferences.
- Provides guidance for integrating the animation logic into modern frameworks like React or Vue.
When to use it
- When you need a premium, restrained animation for hero headlines or section intros.
- When you want to avoid heavy animation libraries for simple text reveal effects.
- When you need to ensure animations are accessible and performant.
- When you want to trigger an animation exactly once upon initial scroll into view.
Advantages
- Lightweight implementation without external dependencies.
- Maintains semantic integrity for screen readers.
- Graceful degradation for non-JS environments.
- Respects system-level reduced-motion settings.
- Cinematic, editorial aesthetic suitable for high-end portfolios.
Usage examples
- “How do I add a staggered word reveal to my hero headline?”
- Apply the data-word-reveal attribute to your h1 tag and include the provided CSS and JS snippets to enable the scroll-triggered animation.
- “Can I use this with Framer Motion?”
- Yes, the skill provides specific motion tokens and timing parameters to replicate the effect using Framer Motion's animation properties.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/mengto/staggered-word-reveal/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip staggered-word-reveal.zip -d .agents/skills
Documentation by Meng To
Staggered Word Reveal
Use When
A short headline, intro, or pull quote should reveal word by word.
The motion should feel editorial, premium, and restrained.
The reveal should trigger only once when the text enters the viewport.
The project does not need heavy GSAP SplitText behavior.
Motion Defaults
Initial state:
opacity: 0,transform: translateY(20px).Final state:
opacity: 1,transform: translateY(0).Duration:
0.8s.Ease:
cubic-bezier(0.16, 1, 0.3, 1).Stagger:
0.06sto0.08sper word. Default to0.07s.Trigger: start around
20%visible, with a slight lower viewport bias.Replay: once only.
HTML
<h1 class="word-reveal" data-word-reveal>
Build interfaces that feel calm, cinematic, and alive.
</h1>CSS
Keep no-JS content visible. Hide only after JavaScript is active and before the text has been split.
.word-reveal {
visibility: visible;
}
html.js .word-reveal[data-word-reveal]:not(.is-ready) {
opacity: 0;
}
.word-reveal__word {
display: inline-block;
opacity: 0;
transform: translate3d(0, 20px, 0);
transition:
opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1),
transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
transition-delay: calc(var(--word-index) * 0.07s);
will-change: opacity, transform;
}
.word-reveal.is-visible .word-reveal__word {
opacity: 1;
transform: translate3d(0, 0, 0);
}
@media (prefers-reduced-motion: reduce) {
html.js .word-reveal[data-word-reveal]:not(.is-ready),
.word-reveal__word {
opacity: 1;
transform: none;
transition: none;
}
}JavaScript
This splitter preserves spaces, avoids innerHTML, exposes the original sentence to screen readers, and unobserves after the first reveal.
document.documentElement.classList.add("js");
function splitWordReveal(element) {
if (element.dataset.wordRevealReady === "true") return;
const text = element.textContent || "";
const parts = text.split(/(\s+)/);
let wordIndex = 0;
element.textContent = "";
element.setAttribute("aria-label", text.trim());
parts.forEach((part) => {
if (!part.trim()) {
element.appendChild(document.createTextNode(part));
return;
}
const word = document.createElement("span");
word.className = "word-reveal__word";
word.setAttribute("aria-hidden", "true");
word.style.setProperty("--word-index", wordIndex);
word.textContent = part;
element.appendChild(word);
wordIndex += 1;
});
element.dataset.wordRevealReady = "true";
element.classList.add("is-ready");
}
function initWordReveals(selector = "[data-word-reveal]") {
const elements = Array.from(document.querySelectorAll(selector));
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduceMotion || !("IntersectionObserver" in window)) {
elements.forEach((element) => {
element.classList.add("is-ready", "is-visible");
});
return;
}
const observer = new IntersectionObserver(
(entries, io) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
entry.target.classList.add("is-visible");
io.unobserve(entry.target);
});
},
{
threshold: 0.2,
rootMargin: "0px 0px -10% 0px",
}
);
elements.forEach((element) => {
splitWordReveal(element);
observer.observe(element);
});
}
document.addEventListener("DOMContentLoaded", () => {
initWordReveals();
});Framework Notes
React/Vue/Svelte: run the splitter after mount, then clean up observer instances on route changes.
Framer Motion: keep the same tokens:
y: 20,opacity: 0, duration0.8, ease[0.16, 1, 0.3, 1], stagger0.06to0.08,once: true.GSAP: use
fromTo(words, { y: 20, opacity: 0 }, { y: 0, opacity: 1, duration: 0.8, ease: "expo.out", stagger: 0.07 }).
Taste Rules
Use on short text: headlines, subheads, labels, and quotes. Avoid long paragraphs.
Stagger words, not letters, for a calmer premium feel.
Keep the offset subtle. Do not add bounce, rotation, or large blur.
Animate
transformandopacityonly.Do not split text containing links, buttons, or meaningful inline markup.
If wrapping is important, initialize after web fonts are ready.
Quick Checks
Text is visible when JavaScript is disabled.
Words begin at
translateY(20px)andopacity: 0.Each word reveals once with a
0.06sto0.08sdelay.Repeated scrolling does not replay the animation.
Reduced-motion users see static readable text.
Files
- ATTRIBUTION.mdAdded by AutoGineer864 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md4.8 KiB
- demo/PROMPT.md2.0 KiB
- demo/index.html16.1 KiB
- demo/preview.jpg69.7 KiB
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Meng To
- Publisher
- Meng To (Community)
- Source
- MengTo/Skills · agent-skills/web-design/staggered-word-reveal · commit 5ea1661
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Meng To or Meng To.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download staggered-word-reveal.zip