Line-by-line Rise
Installation
npx shadcn@latest add https://uibrik.com/r/line-rise.jsonInstall the packages
npm install @chenglou/pretext motionCopy the source into your project
components/uibrik/line-rise/line-rise.tsx"use client"; import { layoutWithLines, prepareWithSegments } from "@chenglou/pretext"; import { motion } from "motion/react"; import { useEffect, useRef, useState } from "react"; import { EASE_OUT, TEXT, type EffectProps } from "../_text-effects/shared"; // 04 — A paragraph rises line by line from behind a clip. Pretext breaks the // lines exactly as the browser would, so each line gets its own mask. // Width and line height scale with the type: 260px and 28px at 20px text. const MASK_WIDTH_EM = 13; const MASK_LINE_EM = 1.4; export function LineRise({ play, text = "Good interfaces reveal themselves one line at a time.", duration = 0.45, stagger = 0.08, }: EffectProps & { text?: string; /** Seconds each line takes to rise. */ duration?: number; /** Seconds between lines. */ stagger?: number; }) { const rootRef = useRef<HTMLDivElement>(null); const [lines, setLines] = useState<string[]>([text]); useEffect(() => { const root = rootRef.current; if (!root) return; let cancelled = false; const style = getComputedStyle(root); const font = `${style.fontWeight} ${style.fontSize} ${style.fontFamily}`; document.fonts.load(font).then(() => { if (cancelled) return; const size = parseFloat(style.fontSize) || 20; const prepared = prepareWithSegments(text, font); setLines(layoutWithLines(prepared, MASK_WIDTH_EM * size, MASK_LINE_EM * size).lines.map((l) => l.text)); }); return () => { cancelled = true; }; }, [text]); return ( <div ref={rootRef} aria-label={text} className={TEXT} style={{ width: `${MASK_WIDTH_EM}em`, lineHeight: `${MASK_LINE_EM}em` }}> {lines.map((line, i) => ( <span key={`${play}-${i}`} aria-hidden className="block overflow-hidden"> <motion.span className="block whitespace-pre" initial={play ? { y: "100%" } : false} animate={{ y: 0 }} transition={{ duration, delay: i * stagger, ease: EASE_OUT }} > {line} </motion.span> </span> ))} </div> ); }
Adds the files to
components/uibrik/line-rise and installs @chenglou/pretext and motion. With the @uibrik namespace set up: npx shadcn@latest add @uibrik/line-rise.Props
LineRise- text
- Type string
- "Good interfaces reveal themselves one line at a time."
- The copy to animate.
- duration
- Type number
- 0.45
- Time for each line to rise.
- stagger
- Type number
- 0.08
- Delay between lines.
- playrequired
- Type number
- –
- Each change plays the effect again; 0 shows its resting state.