Baktiyar Ablaikhan

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).