Baktiyar Ablaikhan

Shimmer text

Source
After bencho.dev ↗
Author
Interaction after bencho.dev
Licence
Original code — interaction after bencho.dev

Loading…

A status line shimmers while it works; hover to hold the light under your pointer.

Code

"use client";

import { AnimatePresence, motion } from "motion/react";
import { useEffect, useRef, useState } from "react";
import { Surface } from "../ui";

const STEPS = ["Reading the brief", "Searching the archive", "Thinking it through", "Writing a reply"];

export default function ShimmerText() {
  const [step, setStep] = useState(0);
  const [beam, setBeam] = useState<number | null>(null);
  const row = useRef<HTMLDivElement>(null);
  const textBox = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const id = window.setInterval(() => setStep((s) => (s + 1) % STEPS.length), 3200);
    return () => window.clearInterval(id);
  }, []);

  return (
    <Surface>
      <div className="flex w-[19em] flex-col gap-[0.8em]">
        <div className="self-end rounded-[1.1em] rounded-br-[0.3em] bg-[#151515] px-[1em] py-[0.6em] text-[0.85em] text-white">
          Summarise last week’s notes?
        </div>
        <div
          ref={row}
          onPointerMove={(e) => {
            const r = textBox.current!.getBoundingClientRect();
            setBeam(e.clientX - r.left);
          }}
          onPointerLeave={() => setBeam(null)}
          onClick={() => setStep((s) => (s + 1) % STEPS.length)}
          className="flex cursor-pointer items-center gap-[0.6em] rounded-[1.1em] rounded-bl-[0.3em] bg-white px-[1em] py-[0.75em] shadow-[0_6px_20px_-12px_rgba(0,0,0,0.25)]"
        >
          <span className="relative grid h-[1.1em] w-[1.1em] place-items-center">
            <span className="absolute inset-0 animate-[bencho-spin_1.2s_linear_infinite] rounded-full border-[0.14em] border-[#e4e3de] border-t-[#151515]" />
          </span>
          <div ref={textBox} className="relative h-[1.4em] flex-1 overflow-hidden">
            <AnimatePresence mode="popLayout" initial={false}>
              <motion.span
                key={step}
                initial={{ y: "100%", opacity: 0 }}
                animate={{ y: "0%", opacity: 1 }}
                exit={{ y: "-100%", opacity: 0 }}
                transition={{ type: "spring", stiffness: 300, damping: 30 }}
                className={`absolute left-0 top-0 whitespace-nowrap text-[1em] font-medium leading-[1.4] ${beam === null ? "bencho-shimmer-text" : ""}`}
                style={
                  beam === null
                    ? undefined
                    : {
                        backgroundImage: `radial-gradient(4em 2em at ${beam}px 50%, #151515 0%, #151515 30%, #a5a49e 100%)`,
                        WebkitBackgroundClip: "text",
                        backgroundClip: "text",
                        color: "transparent",
                      }
                }
              >
                {STEPS[step]}…
              </motion.span>
            </AnimatePresence>
          </div>
        </div>
        <div className="flex gap-[0.4em] pl-[0.4em]">
          {STEPS.map((_, i) => (
            <span key={i} className={`h-[0.3em] rounded-full transition-all duration-500 ${i === step ? "w-[1.4em] bg-[#151515]" : "w-[0.3em] bg-[#c9c8c3]"}`} />
          ))}
        </div>
      </div>
    </Surface>
  );
}
Prompt

Build "Shimmer text", a text animation. What it does: A status line shimmers while it works; hover to hold the light under your pointer. 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).