Rolling Text
Characters roll and flip vertically on hover, with per-character or per-word splitting, direction control, and controlled mode.
Loading preview…
npx shadcn@latest add https://webberui.com/r/rolling-text.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
0.025
0.4
<RollingText />
Installation
npx shadcn@latest add https://webberui.com/r/rolling-text.jsonOr, once registries are configured in components.json, install it as @webberui/rolling-text.
Usage
import { RollingText } from "@/components/ui/rolling-text";
<RollingText text="HOVER TO ROLL" className="text-4xl font-bold" />Controlled mode (parent-triggered)
Inside a link or button, bind active to the parent's hover/focus so keyboard users can trigger the roll too:
const [active, setActive] = React.useState(false);
<a
href="/products"
onMouseEnter={() => setActive(true)}
onMouseLeave={() => setActive(false)}
onFocus={() => setActive(true)}
onBlur={() => setActive(false)}
>
<RollingText text="Products" active={active} />
</a>Props
| Prop | Type | Default | Description |
|---|---|---|---|
text | string | — | The text to roll and flip |
by | "char" | "word" | "char" | Split unit: per character or per word |
direction | "up" | "down" | "up" | Roll direction: which way the original rolls out and the copy rolls in |
stagger | number | 0.025 | Delay between adjacent units (seconds), producing a left-to-right wave |
duration | number | 0.4 | Duration of a single unit's roll (seconds) |
active | boolean | — | controlled mode: the flip state is decided from outside; when omitted, the component manages it internally and triggers on hover |
as | React.ElementType | "span" | The element tag to render |
className | string | — | Appended to the root element's className |
How it works
- Each unit is made of two text layers: the original and its copy swap places inside a
clip-path: inset(0)clipping box, so everything outside the glyph box stays invisible - Clipping uses
clip-pathrather thanoverflow: hidden: the latter moves an inline-block's baseline to the bottom edge of the box, which vertically misaligns it against adjacent inline text - Splitting is done on graphemes, so emoji, flags, and combining characters are never broken apart; whitespace does not participate in the roll, keeping line width and line breaking stable
- The clipping box height equals the line height. With a very small line height (such as
leading-none) descenders may get clipped, so a line-height of at least 1.2 is recommended
Accessibility
- When the user has "reduce motion" enabled at the system level, the text renders statically
- The complete text is exposed
sr-onlyto assistive technology and the entire rolling layer isaria-hidden, so the screen reader announces a whole sentence rather than individual characters - uncontrolled mode responds to mouse hover only; to make keyboard focus trigger it too, switch to controlled mode and bind
activeto the focus/blur of a focusable parent element
Scroll Text Highlight
Reading highlight that turns text from pale grey to solid foreground, character by character (or word by word), as you scroll.
Rolling Number
Odometer-style number wheels — when value changes, each digit springs vertically to its new value; added digits fade in and removed digits collapse.