Five new compound components, loop animations, viewport triggers, sound effects, and full accessibility support — all in one release.
Introduction
Version 3.0.0 of @gfazioli/mantine-text-animate is the biggest release yet. It doubles the number of compound components from five to ten, introduces powerful new animation modes like looping and viewport-triggered animations, and brings first-class accessibility with prefers-reduced-motion support across every component. Whether you need a retro airport departure board, a smooth text morphing transition, or an animated gradient heading, this release has you covered.
What's New
Five New Compound Components
TextAnimate.Gradient — Animated Gradient Text
Apply an animated gradient effect to any text, with full support for Mantine theme colors. Choose from 8 animation directions and pass your own color palette.
<TextAnimate.Gradient colors={['blue', 'cyan', 'green']} speed={1.5} direction="to-right">
Gradient Text
</TextAnimate.Gradient>
The component resolves Mantine color tokens like 'blue.5' via parseThemeColor, so your gradients stay consistent with your theme.
TextAnimate.Highlight — Animated Highlighter Marker
A CSS-only highlighter effect that sweeps a colored marker across your text. No JavaScript animation loop needed — it's all CSS keyframes.
<TextAnimate.Highlight color="yellow" highlightHeight="40%" speed={1}>
Important Text
</TextAnimate.Highlight>
TextAnimate.SplitFlap — Airport Departure Board
Inspired by the classic Solari boards found in train stations and airports, this component flips through a character set with 3D CSS perspective transforms until each character reaches its target.
<TextAnimate.SplitFlap value="DEPARTING 14:30" speed={1.5} />
Each character animates independently with configurable stagger timing. Customize the characterSet, flipDuration, radius, and dividerColor. The companion useSplitFlap hook gives you full programmatic control.
TextAnimate.Morphing — Fluid Text Transitions
Smoothly morph between two strings. The component uses the Longest Common Subsequence (LCS) algorithm to identify shared characters — they stay in place while new characters fade in and old ones fade out.
const [text, setText] = useState('Hello World');
<TextAnimate.Morphing value={text} speed={1} />
<Button onClick={() => setText('Hello Mantine')}>Change</Button>
TextAnimate.RotatingText — Text Carousel
Cycle through an array of strings with animated transitions. Six built-in transition styles: slideUp, slideDown, fade, blur, blurUp, blurDown.
<Text>
I speak{' '}
<TextAnimate.RotatingText
values={['English', 'French', 'Italian', 'Spanish']}
animation="blurUp"
interval={3000}
/>
</Text>
The component renders inline (<span>) so it flows naturally within a sentence. The useRotatingText hook is available for headless usage.
Loop Mode
The base TextAnimate component now supports continuous looping. Set animate="loop" and the animation will cycle: animate in, pause, animate out, pause, and repeat.
<TextAnimate animate="loop" animation="blurDown" loopDelay={2000}>
This text loops forever
</TextAnimate>
The loopDelay prop controls the pause between phases (default: 2000ms).
Viewport-Triggered Animations
Delay animations until the element scrolls into view using IntersectionObserver:
<TextAnimate trigger="inView" triggerOptions={{ threshold: 0.5 }}>
I animate when you can see me
</TextAnimate>
Three trigger modes are available: "mount" (default, same as v2), "inView", and "manual" for full programmatic control.
The useTextAnimate Hook
A new hook for controlling TextAnimate from the outside:
const { animate, setAnimate, replay, isAnimating, key, onAnimationComplete } = useTextAnimate();
<TextAnimate animate={animate} key={key} onAnimationComplete={onAnimationComplete}>
Controlled animation
</TextAnimate>
<Button onClick={replay}>Replay</Button>
<Button onClick={() => setAnimate('out')}>Animate Out</Button>
onAnimationComplete Callback
Unlike onAnimationEnd (which fires per segment), onAnimationComplete fires once when all segments have finished animating — essential for orchestrating sequential animations.
<TextAnimate
by="character"
onAnimationComplete={(direction) => {
if (direction === 'in') showNextElement();
}}
>
Staggered characters
</TextAnimate>
TextTicker Scramble Mode
The existing TextTicker component gains a new "hacker terminal" mode. Set scrambleDuration to make each character cycle through random characters for a fixed duration before settling on the target.
<TextAnimate.TextTicker
value="ACCESS GRANTED"
scrambleDuration={800}
staggerDelay={50}
revealDirection="left-to-right"
/>
Typewriter Enhancements
Three new capabilities for the Typewriter component:
Sound effects — Synthesized mechanical keyboard clicks via Web Audio API:
<TextAnimate.Typewriter value="Click clack..." withSound soundVolume={0.3} />
Per-character callbacks — React to each character as it's typed:
<TextAnimate.Typewriter
value="Hello"
onCharType={(char, index) => console.log(`Typed: ${char}`)}
/>
Custom pauses — Insert dramatic pauses at specific character positions:
<TextAnimate.Typewriter
value="Hello, world!"
pauseAt={{ 5: 1000, 6: 500 }} // Pause 1s after comma, 500ms after space
/>
NumberTicker Custom Formatting
Override the default Intl.NumberFormat with your own formatting function:
<TextAnimate.NumberTicker
value={1234.56}
formatValue={(v) => `$${v.toFixed(2)}`}
/>
Breaking Changes
speed Prop Semantics Standardized
The speed prop now works as a multiplier on all components: speed={1} is normal speed, speed={2} is twice as fast. This is a breaking change for Typewriter, Gradient, and Spinner, which previously used different units.
| Component | v2 Default | v3 Default | Migration |
|---|---|---|---|
| Typewriter |
0.03 (sec/char) |
1 (multiplier) |
Use speed={1} for normal typing speed |
| Gradient |
3 (seconds) |
1 (multiplier) |
Use speed={1} for a 3s cycle |
| Spinner |
10 (sec/rotation) |
2 (multiplier) |
Use speed={2} for a 10s rotation |
// Before (v2)
<TextAnimate.Typewriter value="Hello" speed={0.03} />
<TextAnimate.Gradient speed={3}>Hello</TextAnimate.Gradient>
<TextAnimate.Spinner speed={10}>Hello</TextAnimate.Spinner>
// After (v3)
<TextAnimate.Typewriter value="Hello" speed={1} />
<TextAnimate.Gradient speed={1}>Hello</TextAnimate.Gradient>
<TextAnimate.Spinner speed={2}>Hello</TextAnimate.Spinner>
Spinner Children Type Expanded
Spinner now accepts string | ReactNode[]. If you relied on TypeScript enforcing children: string, the type is now wider. Existing string usage works identically.
Improvements
Full Accessibility Support
Every component now respects prefers-reduced-motion:
-
TextAnimate detects reduced motion in JS to properly fire
onAnimationCompleteand advance loop phases even when CSS animations are disabled - Typewriter shows the final text immediately
- NumberTicker shows the target value instantly
- SplitFlap displays the target text without flipping
ARIA attributes have been added throughout: aria-live="polite" on TextAnimate hidden/static states, automatic role="img" and aria-label on Spinner for string children.
Safety and Robustness
- All
speeddivisions are clamped to a minimum of0.1, preventingInfinityorNaNtimeouts - AudioContext creation/resume is wrapped in try/catch for restricted environments
- Loop timer accumulation is prevented by clearing previous timers
- NumberTicker uses epsilon-based float comparison to reliably detect animation completion
Test Coverage
Expanded from 1 test to 42 tests across 5 suites, covering rendering, props, ARIA attributes, animation directions, text splitting, trigger modes, and more.
Getting Started
# Install or update
npm install @gfazioli/mantine-text-animate@3
# or with yarn
yarn add @gfazioli/mantine-text-animate@3
All new components are accessed as compound components on TextAnimate:
import { TextAnimate } from '@gfazioli/mantine-text-animate';
// New components
<TextAnimate.Gradient>...</TextAnimate.Gradient>
<TextAnimate.Highlight>...</TextAnimate.Highlight>
<TextAnimate.SplitFlap value="..." />
<TextAnimate.Morphing value="..." />
<TextAnimate.RotatingText values={[...]} />
Hooks are exported directly:
import {
useTextAnimate,
useSplitFlap,
useMorphing,
useRotatingText,
} from '@gfazioli/mantine-text-animate';
Links
- npm: npmjs.com/package/@gfazioli/mantine-text-animate
- Documentation: gfazioli.github.io/mantine-text-animate
- GitHub: github.com/gfazioli/mantine-text-animate
- Mantine Extensions Hub: mantine-extensions.vercel.app



Top comments (0)