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