Word relay
TypographyExchanges phrases, words or letters inside a stable wrapping footprint, with interruptible motion and explicit navigation.
Try it
Add to your project
npx shadcn@latest add https://luv-jeri.github.io/cojeev-ui/r/word-relay.jsonThe command includes the component’s shared dependencies. Your project keeps editable source files.
Include the complete example
This example also composes button, native-select, switch, label, typography. Install its parts together:
npx shadcn@latest add https://luv-jeri.github.io/cojeev-ui/r/word-relay.json https://luv-jeri.github.io/cojeev-ui/r/button.json https://luv-jeri.github.io/cojeev-ui/r/native-select.json https://luv-jeri.github.io/cojeev-ui/r/switch.json https://luv-jeri.github.io/cojeev-ui/r/label.json https://luv-jeri.github.io/cojeev-ui/r/typography.jsonUsing word relay
Pass words and optionally control index/onIndexChange. All candidates share one wrapping layout footprint, so the surrounding content stays in place.
Choose split="phrase" (default), "word" or "grapheme". staggerFrom="first", "last", "center" or "edges" sets the logical arrival order; direction="up" or "down" sets travel. Stagger is milliseconds, evenly bounded to a 500ms window.
Automatic changes are opt-in through auto; pair with a pause control. interval is 1200–60000ms and always allows the transition plus at least 500ms at rest. duration is 0–1600ms. loop=false stops at the last phrase.
Use controlsRef for next(), previous(), jumpTo(index) and reset(). The normal ref still returns the span. Controlled navigation requests onIndexChange; the parent must apply the new index. reset returns to defaultIndex.
Use tone="inherit" inside a heading, or a native accent tone for an organic highlight.
API
Compose the exported parts below. The prop types are read from the React source during the registry build.
Native element or underlying primitive props are inherited where declared, including accessible labels, event handlers and refs.
WordRelayProps
| Prop | Type | Required |
|---|---|---|
words | readonly string[] | Yes |
indexA controlled index. Omit for the internal timer. | number | No |
defaultIndex | number | No |
autoOpt in to automatic changes and provide a nearby pause control. | boolean | No |
loopWhen false, automatic and manual navigation stop at the first/last phrase. | boolean | No |
paused | boolean | No |
interval | number | No |
duration | number | No |
split | "phrase" | "word" | "grapheme" | No |
staggerMilliseconds between units, compressed to a maximum 500ms stagger window. | number | No |
staggerFrom | RelayOrder | No |
direction | "up" | "down" | No |
controlsRefKeeps the normal ref pointing to the span; navigation has its own optional ref. | React.Ref<WordRelayHandle> | No |
tone | "inherit" | "pink" | "olive" | "blue" | "yellow" | No |
onIndexChange | (index: number) => void | No |
Accessibility
Only the current phrase is exposed to assistive technology; animated layers are hidden. Add aria-live only if the content warrants announcements.
Hover and focus pause automatic playback. Reduced motion, shared Motion Off, Flow Off, hidden tabs and offscreen content suspend the timer.
Empty words produce an empty span, and out-of-range indexes clamp to the available phrases.
Grapheme grouping preserves emoji and combining marks; use word or phrase grouping to preserve joining in scripts such as Arabic.
States and appearance
For conditional content, keep a MotionPresence boundary around keyed MotionSurface children. The shared entrance and exit states work with native primitives and respect your motion preference.
Try pointer and keyboard interaction, and switch the appearance control in the navigation to compare light and dark mode. Motion respects reduced motion preferences.