Skip to content
WANCRAFT

C.163Typography & Text

Text Prism

Words printed on the faces of a 3D prism that tumbles to the next word, with the width reserved for the longest.

NewReactCSSTailwindintermediate

Overview

Text Prism places words on the faces of a triangular or square CSS 3D prism and tumbles it to the next word on an interval, with a slight overshoot as it lands. Faces sit at translateZ of the prism's apothem, computed from the measured line height (or the reserved width when spinning around the y axis), and faces are refilled as they turn out of view, so any number of words works. All words are stacked invisibly in one grid cell to reserve the width of the longest, so the surrounding sentence never reflows. It pauses on hover, offscreen and in hidden tabs, can stop after a set number of loops, and shows a small pause button while it loops. The prism is aria-hidden and screen readers hear the full list once; reduced motion swaps the rotation for a gentle crossfade.

01Installation

Method 01 · Registry

CLI verified

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

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

Method 02 · Copy and paste

  1. 1. No extra dependencies — React and Tailwind CSS only.
  2. 2. Copy the source from the Code tab into components/ui/text-prism.tsx.
  3. 3. Import it and adjust props.
Dependencies
None
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 { TextPrism } from "@/components/ui/text-prism";

export function TextPrismExample() {
  const words = ["considered", "tactile", "honest", "quick"];

  return (
    <p className="text-5xl font-semibold tracking-tight">
      Interfaces that feel{" "}
      <TextPrism words={words} />
    </p>
  );
}

03Props

PropTypeDefaultDescription
wordsstring[]—Required. Words shown in order on the prism faces.
intervalnumber2400Time each word stays in front, in ms (including the turn).
durationnumber700Length of one turn in ms.
shape"triangle" | "square""triangle"Three or four faces.
axis"x" | "y""x"x tumbles (next word rolls up from below); y spins sideways.
perspectivenumber600CSS perspective in px.
variant"accent" | "plain" | "solid""accent"Accent-coloured words, inherited colour, or words on solid accent faces.
pauseOnHoverbooleantrueHold the current word while a mouse is over it.
loopsnumber0Stop on the last word after this many passes (0 = forever).
showPauseButtonbooleantrueSmall pause / play button shown while the prism loops.
accentstring"#FF7448"Word colour, solid face colour and focus ring.
srLabelstringwords joined by commasText screen readers hear instead of the rotating words.

04Features

  • 01Real CSS 3D prism: faces at rotateX/rotateY with translateZ of the apothem, preserve-3d and hidden backfaces
  • 02Faces are refilled as they turn away, so any number of words works on three or four faces
  • 03Width reserved for the longest word with an invisible stacked grid, so the sentence never reflows
  • 04Face radius derived from the measured line box (ResizeObserver), with em fallbacks for SSR
  • 05Pauses on hover, offscreen (IntersectionObserver) and in hidden tabs; optional loop count
  • 06Overshooting turn easing and fading or darkening of the faces turning away
motion · tweenmotion · continuousinput · hoverinput · viewportinput · click

05Accessibility

  • The prism is aria-hidden; a visually hidden span reads the full word list once (customisable with srLabel).
  • While looping, a pause / play button (aria-label "Pause word rotation" / "Resume word rotation") lets anyone stop the motion (WCAG 2.2.2).
  • With a loop count the animation stops on its own and the button disappears.
  • The pause button has a visible accent focus ring.
  • prefers-reduced-motion replaces the 3D turn with a crossfade between words.

06Limitations

  • Faces do not wrap: keep each word to a single line.