Scramble Hover

Text that scrambles into random glyphs on hover, then reveals the original word letter-by-letter — a small piece of editorial motion that earns a second look.

craft-bits
Customize
Animation
50ms
10
Behavior

Installation

npx shadcn@latest add https://craftbits.dev/r/scramble-hover.json

Usage

import { ScrambleHover } from "@craft-bits/core";
 
<ScrambleHover text="hover me" />

Understanding the component

  1. Two stacked spans. The outer wrapper renders an invisible sr-only copy of the real text so screen readers always announce the actual word. The visible copy is aria-hidden so assistive tech skips it during the scramble.
  2. Per-character spans. The visible copy splits text into individual <span> elements so each character can have its own class — either className (revealed) or scrambledClassName (still scrambling).
  3. Reveal modes. Non-sequential (default) cycles every char for maxIterations frames then snaps. Sequential reveals one position at a time, starting from revealDirection.
  4. Animation loop. A requestAnimationFrame loop ticks every scrambleSpeed ms. Each tick re-randomizes still-scrambling chars.
  5. Reduced motion. When prefers-reduced-motion: reduce is set, the effect short-circuits — no hover handler, no animation frame.

Props

PropTypeDefaultDescription
textstringrequiredThe final reveal text.
scrambleSpeednumber50Milliseconds per scramble frame.
maxIterationsnumber10Frames before reveal completes (non-sequential mode).
sequentialbooleanfalseReveal letter-by-letter instead of all at once.
revealDirection'start' | 'end' | 'center''start'Order chars reveal in sequential mode.
useOriginalCharsOnlybooleanfalseShuffle only the text's own characters.
charactersstringglyph poolCharacter pool to scramble from.
classNamestringClass for revealed text.
scrambledClassNamestringClass for currently-scrambling characters.

Accessibility

  • Real text lives inside .sr-only so screen readers announce the unscrambled word.
  • Scrambled characters use aria-hidden="true".
  • Animation is fully disabled when prefers-reduced-motion: reduce is set.

Credits