Skip to content
WANCRAFT

C.132Typography & Text

Wave Text

Letters that ripple in a springy wave from the letter nearest the pointer — on hover, focus, in view or continuously.

NewReactMotionTailwindintermediate

Overview

Wave Text sends a ripple through a line of type. Every letter gets a spring kick: an initial velocity calculated from the spring's stiffness, damping and mass so it lifts by exactly the amplitude you set, then settles with as much bounce as the damping allows. On hover the wave spreads outward from the letter nearest the pointer; on focus of the surrounding link or button, a tap, or when scrolled into view it starts from the first letter; and it can loop while hovered, loop while visible or ripple continuously. Letters can also scale, tilt and flash an accent colour as the wave passes. The text is exposed once to screen readers while the animated letters are hidden, words never break mid-word, loops pause off-screen and in hidden tabs, and with reduced motion the text stays still.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/wave-text.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/wave-text.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/wave-text.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { WaveText } from "@/components/ui/wave-text";

export function WaveTextExample() {
  return (
    <h2 className="text-5xl font-semibold tracking-tight">
      <WaveText />
    </h2>
  );
}

03Props

PropTypeDefaultDescription
textstring"Making waves"Text to render.
as"span" | "p" | "h1" | "h2" | "h3" | "div""span"Element to render.
trigger"hover" | "view" | "continuous""hover"hover also responds to taps and to focus of the closest focusable ancestor; view plays when 50% visible; continuous loops while visible.
loopbooleanfalseRepeat while hovered (hover) or while visible (view).
loopDelaynumber1200Pause between repeated waves, in ms.
amplitudenumber10Peak lift of each letter in px (exact — the kick is solved from the spring).
staggernumber40Delay between neighbouring letters, in ms.
spring{ stiffness: number; damping: number; mass: number }{ stiffness: 320, damping: 11, mass: 0.6 }Spring each letter rides; lower damping bounces more.
scalenumber1Peak scale of a letter (1 = none).
rotatenumber0Peak tilt of a letter in degrees.
colorstring | null"#FF7448"Colour letters flash as the wave passes; null disables it.
...propsHTMLAttributes<HTMLElement>—Forwarded to the root element.

04Features

  • 01Each letter gets a spring kick solved so its first peak equals the amplitude
  • 02On hover the stagger spreads outward from the letter nearest the pointer
  • 03Hover, tap, focus of a wrapping link or button, in view, loop and continuous triggers
  • 04Optional scale, tilt and accent-colour flash per letter
  • 05Words never break mid-word; grapheme-aware splitting keeps emoji and accents intact
  • 06Loops pause when off-screen or the tab is hidden; nothing re-renders per frame
motion · springmotion · staggermotion · continuousinput · hoverinput · focusinput · viewport

05Accessibility

  • The text is exposed once as visually hidden text; the animated letters are aria-hidden.
  • Inside a link or button, keyboard focus plays the wave just like hover.
  • Purely decorative motion: it never changes layout or reading order.
  • With prefers-reduced-motion the letters stay still.