Skip to content

Wave Text

Bouncy text where letters hop one after another in a wave — always, or on hover/focus of the text or a parent .group. Pure CSS.

soft-flat/effect/wave-text
Open ↗

Source

import { cn } from "@/lib/utils";

export interface WaveTextProps {
  text?: string;
  /** Wave constantly, or only on hover/focus of the element or a parent .group. */
  trigger?: "always" | "hover";
  as?: "h1" | "h2" | "h3" | "p" | "span";
  className?: string;
}

/** Bouncy text: letters hop one after another in a wave, constantly or on hover. Screen readers read the word normally. Pure CSS. */
export function WaveText({ text = "Hooray!", trigger = "hover", as: Tag = "span", className }: WaveTextProps) {
  return (
    <Tag className={cn("sf-wave-text inline-block font-da-display font-extrabold", trigger === "always" && "is-always", className)} aria-label={text}>
      {Array.from(text).map((ch, i) => (
        <span key={i} aria-hidden className="sf-wave-letter inline-block" style={{ animationDelay: `${i * 60}ms` }}>
          {ch === " " ? " " : ch}
        </span>
      ))}
      <style href="sf-wave-text" precedence="default">{`
        @keyframes sf-wave-letter { 0%, 60%, 100% { translate: 0 0 } 30% { translate: 0 -0.3em } }
        .sf-wave-text.is-always .sf-wave-letter,
        .sf-wave-text:hover .sf-wave-letter,
        .group:hover .sf-wave-text .sf-wave-letter,
        .group:focus-visible .sf-wave-text .sf-wave-letter { animation: sf-wave-letter 1s ease-in-out infinite }
        @media (prefers-reduced-motion: reduce) { .sf-wave-letter { animation: none !important } }
      `}</style>
    </Tag>
  );
}

export default WaveText;

modules/soft-flat/effect/wave-text/index.tsx

Props

PropTypeDefaultDescription
textstring"Hooray!"Text.
trigger"always" | "hover""hover"When to animate.
asstring"span"Element.

Other text variants in Soft Flat

Text in other art directions

Animated

Glitch Text

Print-misregistration glitch: two offset copies of the text in accent and secondary colors jitter in horizontal slices, constantly or only on hover/focus. Pure CSS, blend mode adapts to dark.

BrutalistText
Animated

Marquee Band

Tilted full-bleed ticker band with giant uppercase words scrolling infinitely between ruled strokes. Seamless loop, direction and speed props, three color tones.

BrutalistText
Animated

Scramble Text

Decoder-style mono text: random glyphs resolve left to right into the final string when it scrolls into view, and again on hover or focus. Great for version numbers, stats and labels.

BrutalistText
Animated

Typewriter Text

Headline with a static prefix and a rotating last word that is typed character by character, held, deleted and replaced, followed by a blinking block cursor.

BrutalistText
Animated

Count Up

Number that counts up with an expo-out ease and tabular digits when scrolled into view; prefix, suffix, decimals and locale-aware thousands separators.

MinimalText
Animated

Gradient Text

Inline text filled with a slowly flowing gradient built from the primary and accent tokens (bg-clip-text); animated or static. Pure CSS.

MinimalText
Animated

Rotating Words

Headline with a static prefix and a highlighted word that slides up to the next one on an interval, inside a slot whose width animates to fit each word.

MinimalText
Animated

Text Shimmer

Loading/live text effect: a light band sweeps across muted text using a clipped gradient. For "thinking…", "generating…" or live status labels. Pure CSS, server-safe.

MinimalText
Animated

Digit Roll

Odometer-style number: each digit rolls vertically through 0–9 to its value (staggered from the left) when it enters the viewport, in tabular display figures.

Mono CleanText
Animated

Outline Fill

Outlined display text (hairline stroke, transparent fill) that fills solid ink from bottom to top on hover/focus via a clipped duplicate layer — for big navigation links. Pure CSS.

Mono CleanText
Animated

Scramble Text

Mono text that resolves from random glyphs to the real string left-to-right when it enters the viewport (and again on hover); final text is always exposed to assistive tech.

Mono CleanText
Animated

Tracking In

Headline whose letter-spacing contracts from very wide to the tight display tracking while fading in — a calm, precise entrance for short words.

Mono CleanText
Animated

Count Up

Number that counts up with an ease-out when scrolled into view, formatted with thousands separators (prefix/suffix, decimals); tabular figures prevent jitter. Final value for screen readers.

Neo CorporateText
Animated

Shimmer Text

Text filled with a primary→accent-fg gradient and a slow light sweep passing across it every few seconds — for a single highlighted phrase in a headline. Pure CSS.

Neo CorporateText
Animated

Typed Words

Headline with a rotating keyword typed and erased character by character in primary, with a blinking caret; reduced motion swaps whole words. Full sentence exposed to screen readers.

Neo CorporateText
Animated

Word Reveal

Headline whose words slide up from behind a mask one after another when scrolled into view — crisp, no blur or bounce. The element keeps its plain text for screen readers.

Neo CorporateText
Animated

Drawn Underline

Hand-drawn underline that draws itself (SVG pathLength) beneath a word when it scrolls into view — a loose, slightly wavy pen stroke.

Organic SoftText
Animated

Italic Swap

Headline with a rotating italic serif word: each word slides up out of a mask and the next rises in with a soft ease; the full sentence is available to screen readers.

Organic SoftText
Animated

Letter Rise

Letters drift up from below with a gentle fade, one after another like seeds sprouting, when the text scrolls into view; words never break mid-letter.

Organic SoftText
Animated

Marker Highlight

Highlighter-pen effect: a soft tinted band sweeps left-to-right behind the words when they scroll into view; works across line wraps (box-decoration-break) and text stays fully legible.

Organic SoftText