Dispersed Text
v1.1.1FreeTypography
An advanced interactive typography component where text morphs liquidly on content change and disperses into custom particle fields (flow, explode, attract, wind, gravity, vortex) on interaction. Supports solid or gradient colors, speed-based color mapping, neon glow, custom shapes, and manual animation triggers.
Dispersed Text
Pointera11y
Installation
Step 1. Initialize ProjectOne-time setup
Run once in your project root to configure paths and dependencies:
npx vectorvesper initStep 2. Add ComponentInstalls source & assets
Downloads and registers the component in your project:
npx vectorvesper add dispersed-textStep 3. Required DependenciesAuto-installed with CLI
If installing manually or managing your package lock:
npm install framer-motionPackages:framer-motion
Usage
Import
Usage
This component respects
prefers-reduced-motion and provides a graceful fallback.Props
Text rendered as a cloud of particles that scatters when you touch it and reassembles when you leave. Six ways for it to come apart, four particle shapes, and colour that can follow speed.
<DispersedText />
The whole block. Sizes itself to its container.
| Prop | Type | Default | Description |
|---|---|---|---|
text* | string | — | The words to render. Required. |
interactionMode | "flow" | "explode" | "attract" | "vortex" | "wind" | "gravity" | "flow" | How the particles react. Flow pushes them gently aside, explode throws them outward, attract pulls them in, vortex spins them around the pointer, wind blows them along windAngle, gravity drops them. |
trigger | "hover" | "click" | "manual" | "hover" | What sets it off. With `manual` the component ignores the pointer and you drive it through isDispersed or progress. |
isDispersed | boolean | false | Scattered or assembled, when trigger is `manual`. |
progress | number | 0 | Position between assembled and scattered, 0–1, when you want to drive it from a scroll or timeline rather than a boolean. |
windAngle | number | 0 | Direction the wind blows, in degrees. Only used by the `wind` mode. |
spring | number | 0.08 | How hard particles are pulled back to their letter. Higher snaps back; lower drifts home slowly. |
meltSpeed | number | 0.06 | How quickly they leave the letter when disturbed. |
swirl | number | 1.2 | How much they curl around the pointer rather than moving straight away from it. |
swirlRadius | number | 160 | How far from the pointer, in pixels, that influence reaches. |
streak | number | 5 | Length of the trail each particle leaves. 0 draws them as points. |
turbulence | number | 1 | Random jitter layered over the motion. 0 turns the jitter off. |
fontSize | number | fitted to the box | Fixed size in pixels. Left unset, the text is fitted to its container. |
fitWidth | number | 0.82 | Fraction of the container width the text should fill when it is being fitted. |
fontFamily | string | the system sans stack | Face used to lay out the letters before they become particles. |
fontWeight | number | string | 700 | Weight of that face. Heavier gives more particles to work with. |
lineHeight | number | 1.15 | Line spacing for multi-line text. |
gap | number | 6 | Spacing between sampled particles, in pixels. Lower packs them denser and costs more to draw. |
dotSize | number | 1.7 | Size of one particle. |
shape | "circle" | "square" | "triangle" | "star" | "circle" | What each particle is drawn as. |
color | string | "#7EACB5" | Particle colour when colorMode is `solid`. |
colorMode | "solid" | "gradient" | "speed" | "solid" | One colour, a gradient across the text, or colour driven by how fast each particle is moving. |
gradientColors | string[] | ["#7EACB5", "#E07B9B"] | Colours the gradient runs through, when colorMode is `gradient`. |
gradientDirection | "horizontal" | "vertical" | "diagonal" | "horizontal" | Which way that gradient runs. |
speedColor | string | "#FF007A" | Colour a particle reaches at full speed, when colorMode is `speed`. It blends toward this from `color`. |
glow | boolean | false | Adds a bloom around each particle. |
glowColor | string | the particle colour | Colour of that bloom. Follows the particle when unset. |
glowBlur | number | 8 | Spread of the bloom in pixels. |
maxDpr | number | 2 | Pixel-ratio ceiling. Lower it to 1 on particle-heavy layouts to halve the fill cost. |
debug | boolean | false | Draws the sampling grid over the text, for tuning gap and dotSize. |
className | string | "" | Applied to the wrapper. |
style | React.CSSProperties | — | Inline styles merged onto the wrapper. |
aria-label | string | the text prop | Label announced for the block. Falls back to `text`, so it is usually already correct. |
Examples
Scatter on hover
import DispersedText from "@/components/vv/dispersed-text/DispersedText";
<div className="h-64">
<DispersedText text="DISPERSE" />
</div>Blown sideways, coloured by speed
<DispersedText
text="WINDWARD"
interactionMode="wind"
windAngle={30}
colorMode="speed"
color="#7EACB5"
speedColor="#FF007A"
streak={12}
/>Driven by scroll instead of the pointer
<DispersedText
text="ASSEMBLE"
trigger="manual"
progress={scrollProgress}
interactionMode="gravity"
/>The particles keep moving under reduced motion. Set trigger="manual" with progress={0} when the preference is set — the text stays fully assembled and readable, with no motion at all.
Cost scales with particle count, which
gap controls: halving it roughly quadruples the work. If a long line feels heavy, raise gap before touching anything else, and drop maxDpr to 1.Client component. It measures text and draws to a canvas, neither of which exists on the server.