Text rise
- Source
- After bencho.dev ↗
- Author
- Interaction after bencho.dev
- Licence
- Original code — interaction after bencho.dev
Loading…
Letters rise into place; hover a word to lift it again, click to replay.
Code
"use client";
import { motion } from "motion/react";
import { useState } from "react";
import { Surface } from "../ui";
const LINES = ["Small things,", "carefully made."].map((line, li, all) => {
const before = all.slice(0, li).reduce((n, l) => n + l.split(" ").length, 0);
return line.split(" ").map((w, wi, words) => ({ w, index: before + wi, last: wi === words.length - 1 }));
});
function Word({ word, delay, round }: { word: string; delay: number; round: number }) {
const [again, setAgain] = useState(0);
return (
<span
className="inline-block overflow-hidden pb-[0.08em] align-bottom"
onPointerEnter={() => setAgain((n) => n + 1)}
>
{word.split("").map((ch, i) => (
<motion.span
key={`${round}-${again}-${i}`}
className="inline-block"
initial={{ y: "110%", rotate: 8, opacity: 0 }}
animate={{ y: "0%", rotate: 0, opacity: 1 }}
transition={{
type: "spring",
stiffness: 260,
damping: 22,
delay: again ? i * 0.025 : delay + i * 0.035,
}}
>
{ch}
</motion.span>
))}
</span>
);
}
export default function TextRise() {
const [round, setRound] = useState(0);
return (
<Surface>
<button type="button" onClick={() => setRound((r) => r + 1)} className="cursor-pointer px-[1em] text-center">
{LINES.map((line, li) => (
<span key={li} className="block font-serif text-[3.2em] leading-[1.02] tracking-[-0.01em] text-[#151515]">
{line.map(({ w, index, last }) => (
<span key={index}>
<Word key={round} word={w} delay={index * 0.12} round={round} />
{last ? "" : " "}
</span>
))}
</span>
))}
</button>
<p className="pointer-events-none absolute bottom-[6%] font-mono text-[0.7em] text-[#8a8984]">hover a word · click to replay</p>
</Surface>
);
}
Prompt
Build "Text rise", a text animation. What it does: Letters rise into place; hover a word to lift it again, click to replay. Write it as a single self-contained React component in TypeScript that fills its parent (w-full h-full), styled with Tailwind CSS, using motion. Technique: an HTML canvas, the Motion library, pointer tracking. It sits on a light background, in a 16:10 frame. Respect prefers-reduced-motion with a still state, pause work when the element is off screen, and keep it accessible. Original: https://bencho.dev (Interaction after bencho.dev, Original code — interaction after bencho.dev).