Sora UI

Text Roll

A per-character vertical text roll animation for label swaps and micro-interactions, powered by Motion. React.

Made by Axyl

Per-character vertical text roll — click the preview button to trigger a Copy → Copied swap.

Installation

File Structure

text-roll.tsx

Usage

TextRoll does not animate on hover or click by itself — update the text prop (same as SlotText from slot-text):

import { useState } from 'react';
import { TextRoll } from '@/components/sora-ui/texts/text-roll';

export default function CopyButton() {
  const [copied, setCopied] = useState(false);

  return (
    <button
      type="button"
      onClick={() => {
        setCopied(true);
        window.setTimeout(() => setCopied(false), 1400);
      }}
    >
      <TextRoll text={copied ? 'Copied' : 'Copy'} />
    </button>
  );
}

Click the preview button to see Copy → Copied roll. Hover alone does not trigger the effect.

Direction, stagger, and bounce:

<TextRoll
  text={label}
  direction="up"
  stagger={60}
  bounce={0.8}
  className="font-mono text-sm font-semibold"
/>

Chromatic sweep on roll — pass a color function or use the built-in helper:

import { TextRoll, chromatic } from '@/components/sora-ui/texts/text-roll';

<TextRoll
  text={label}
  options={{
    direction: 'up',
    skipUnchanged: false,
    color: chromatic({ from: 190 }),
  }}
/>;

For aligned short labels (CopyCopied), keep skipUnchanged at the default true so shared letters stay put. Turn it off when lengths differ so the whole line rolls uniformly.

Props

PropTypeDefault
text?
string
-
options?
TextRollOptions
-
direction?
"up" | "down"
"down"
stagger?
number
45
duration?
number
300
bounce?
number
0.6
skipUnchanged?
boolean
true
chromatic?
boolean
false
className?
string
-

When to Use

Use TextRoll for micro-interactions where a label changes state: copy buttons, submit confirmations, tab switches, quantity controls, or any short string that toggles between two values. The per-character roll gives discrete state changes a physical weight that a plain text swap lacks.

Keep labels short (1–10 characters) for the effect to be legible. For longer text swaps or multi-word labels, consider TextMorph or a simple fade.

Accessibility

TextRoll animates characters via CSS transforms — the text string is always in the DOM. When using it inside an interactive element, ensure the parent has a clear accessible label if the visible text may be ambiguous mid-animation. The bounce parameter controls personality, not speed; total animation time is duration + (text.length × stagger) in milliseconds. When prefers-reduced-motion: reduce is set, the component skips the roll animation via useReducedMotion() and renders the text directly — no extra configuration needed.

Credits

Inspired by slot-text.

Built by Axyl. A motion-first component registry for React.

Last updated: 7/26/2026