staggered-word-reveal — Claude Code skill
Builds word-by-word text reveals as content enters the viewport. Use it for short headlines, section intros, quotes, and hero copy.
npx skills add MengTo/Skills --skill staggered-word-reveal -a claude-codeWhat staggered-word-reveal does
This skill defines a word-by-word reveal for text that enters the viewport. Each word starts transparent and shifted vertically, then transitions into place with a 0.06s–0.08s delay between words.
Its script splits text without innerHTML, preserves whitespace, and exposes the original sentence to screen readers through aria-label. IntersectionObserver triggers the reveal once, then stops observing the element. Text stays visible when JavaScript is disabled, while reduced-motion settings show readable static content.
For React, Vue, and Svelte, run the splitter after mount and clean up observers on route changes. This is a poor fit for long paragraphs or text containing links, buttons, or meaningful inline markup.
What it helps with
- reveal headline words in sequence
- trigger text motion when a block enters the viewport
- keep content readable without JavaScript
- respect reduced-motion preferences
Example prompts for the agent
- Add a word-by-word reveal to this
h1with[data-word-reveal]. - Animate this short quote with
IntersectionObserverand prevent replay after scrolling. - Set up
word-revealandword-reveal__wordstyles with reduced-motion handling.
How to install staggered-word-reveal
1Skills CLI (Vercel)
npx skills add MengTo/Skills --skill staggered-word-reveal -a claude-code2Manually: put the files in place
~/.claude/skills/staggered-word-reveal/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/staggered-word-reveal/
1Skills CLI (Vercel)
npx skills add MengTo/Skills --skill staggered-word-reveal -a codex2Manually: put the files in place
.agents/skills/staggered-word-reveal/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add MengTo/Skills --skill staggered-word-reveal -a cursor2Manually: put the files in place
.cursor/skills/staggered-word-reveal/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install MengTo/Skills staggered-word-reveal2Manually: put the files in place
.github/skills/staggered-word-reveal/SKILL.md1Skills CLI (Vercel)
npx skills add MengTo/Skills --skill staggered-word-reveal -a gemini-cli2Platform CLI
gemini skills install https://github.com/MengTo/Skills --path agent-skills/web-design/staggered-word-revealManually: put the files in place
.devin/skills/staggered-word-reveal/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add MengTo/Skills --skill staggered-word-reveal -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about staggered-word-reveal
How do I install staggered-word-reveal in Claude Code?
Run npx skills add MengTo/Skills --skill staggered-word-reveal -a claude-code twice. The skill file is placed at ~/.claude/skills/staggered-word-reveal/SKILL.md.
What does staggered-word-reveal do?
It helps create restrained word-by-word text reveals for short copy. The effect runs when the element enters the viewport and does not replay on later scrolling.
What happens when reduced motion is enabled?
With prefers-reduced-motion: reduce, words remain visible without transitions or movement. The same static treatment is used when IntersectionObserver is unavailable.
Source text
SKILL.md · MIT
---
name: staggered-word-reveal
description: Create subtle editorial word-by-word text reveal animations where each word fades and rises into place once it enters the viewport. Use for premium portfolio headlines, hero copy, section intros, and short marketing text that needs a cinematic staggered reveal with IntersectionObserver or in-view detection.
---
# 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.06s` to `0.08s` per word. Default to `0.07s`.
- Trigger: start around `20%` visible, with a slight lower viewport bias.
- Replay: once only.
## HTML
```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.
```css
.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.
```js
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`, duration `0.8`, ease `[0.16, 1, 0.3, 1]`, stagger `0.06` to `0.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 `transform` and `opacity` only.
- 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)` and `opacity: 0`.
- Each word reveals once with a `0.06s` to `0.08s` delay.
- Repeated scrolling does not replay the animation.
- Reduced-motion users see static readable text.
© MengTo · MIT · MengTo/Skills