Each word, on its own.
The default mode. Words slide up sequentially with a 30ms stagger. Drop in a
<split-text> tag and it animates as soon as it scrolls into view —
nothing else to wire up.
Letter by letter.
Use split="chars" for grapheme-level animation. Emoji and accented characters
stay intact — the splitter uses Intl.Segmenter internally so nothing breaks
mid-codepoint. Pair it with effect="magnetic" across a longer passage and
every character flies in from its own scattered start.
Every line, together.
split="lines" measures word positions after layout, groups them by their
rendered top, and animates entire lines together. Inline markup stays in place — no
words are ever re-parented, so links and emphasis survive the split.
Links still link.
Wrap whatever you want — <em>, <strong>,
<a>, <br>. The splitter walks text nodes only, so
inline tags are never moved or unwrapped. Try clicking the link below.
It plays nicely with real HTML —
links remain links,
emphasis stays emphatic, and line breaks are honored as actual line boundaries.
Or fall in from above.
Set effect="drop" to mirror the default rise — words descend into place
instead of rising up. Same timing knobs, same fade.
Slide in from either side.
effect="slide-right" sweeps each unit in from the right;
slide-left mirrors it. Pairs nicely with split="chars" for a
typewriter-meets-marquee feel.
<split-text effect="slide-left" split="chars">…</split-text>
Bloom into focus.
effect="bloom" starts each unit blurred and slightly scaled-down, then
sharpens it in place. No translate — letters appear right where they belong and just
come into focus. Tune the starting state with --split-text-blur and
--split-text-scale.
/* override via CSS custom properties */
style="--split-text-blur: 12px; --split-text-scale: 0.4"
Spin into place.
Two 3D variants. spin-x tilts each unit up from the baseline (rotateX,
origin bottom). spin-y swings each unit open like a door
(rotateY, origin left). The host sets perspective: 2000px by
default — tune via --split-text-perspective, and shift the pivot with
--split-text-origin.
<split-text effect="spin-y" split="chars">…</split-text>
/* override pivot & perspective via CSS */
style="--split-text-origin: center; --split-text-perspective: 600px"
Pulled into place.
effect="magnetic" scatters each character — every piece starts shifted off to
the right by a random distance, at a random height, blurred and transparent. An ease-in
curve makes them drift slowly then snap home, like filings pulled to a magnet. Unlike the
other effects, the pieces are never clipped, so they can travel well outside their box.
Built for split="chars".
Tune the timing.
Adjust delay, stagger, and duration with the sliders.
Easing with feeling.
Pass any CSS easing through the easing attribute, or override the
--split-text-easing custom property. Below: three different curves on the
same line.
A subtler rise.
--split-text-distance controls how far each unit travels. Pair a short
travel with a meaningful initial delay for a slow, considered intro.
Drive it yourself.
Set trigger="manual" and call .reveal() from JavaScript when you
want the animation to fire. Useful for orchestrating a sequence with other animations,
or replaying on demand.
Listen in.
Each component fires split-text:start and split-text:complete.
Open your console and scroll past — every reveal logs its mode and unit count.
Choose when it fires.
By default text waits until the element is 20% of the viewport above the
bottom edge — so it doesn't animate while still peeking in from below. Override
offset with a pixel value, a different percentage, or "0" to
disable. The line below waits until it's 400px above the bottom edge before animating.
<split-text offset="400px">…</split-text> // custom pixel value
<split-text offset="0">…</split-text> // disable, fire on first intersection
The whole API, on one screen.
Eight attributes, nine custom properties, two methods, two events. That's everything.
Attributes
splitwordswords · chars · lineseffectriserise · drop · slide-right · slide-left · bloom · spin-x · spin-y · magneticdelay0stagger30duration800easingcubic-bezier(0.16, 1, 0.3, 1)triggervisiblevisible · load · manualoffset20%"200px", "20%"; set "0" to disable)CSS Custom Properties
--split-text-duration800ms--split-text-easingcubic-bezier(0.16, 1, 0.3, 1)--split-text-distance100%--split-text-stagger30ms--split-text-delay0ms--split-text-perspective2000px--split-text-origintransform-origin for spin (defaults: bottom for spin-x, left for spin-y)--split-text-blur2px--split-text-scale0.7Methods & Events
reveal()split()split-text:start{ split, count }split-text:completeAccessibility
When the OS preference is reduce motion, the component skips animation
entirely and renders content immediately — no transforms, no blur, no fade. The host's
aria-label is set from the original plain text, so screen readers read the
sentence once instead of one wrapped span at a time. Generated word and character
wrappers are marked aria-hidden, except inside interactive ancestors
(<a>, <button>, etc.) where the accessible name
must be preserved.
Browser support
Modern browsers with custom elements + IntersectionObserver: Chrome 64+,
Firefox 67+, Safari 12.1+, Edge 79+. Intl.Segmenter is used when available
for grapheme-correct character splitting; older Safari falls back to spread iterator.