Spotlight Follow Cards
Original · freeFeature cards where a soft radial spotlight and a glowing border chase the cursor on hover, with a staggered scroll reveal and an animated shimmer line.
Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.
npx shadcn@latest add https://webinnoventix.com/r/card-spotlight-follow.json"use client";
import { useRef, useState, type PointerEvent as ReactPointerEvent } from "react";
import { motion, useMotionTemplate, useMotionValue } from "motion/react";
const EASE = [0.16, 1, 0.3, 1] as const;
type SpotItem = {
title: string;
copy: string;
icon: "shield" | "bolt" | "chart";
};
const ITEMS: SpotItem[] = [
{
title: "Guarded by design",
copy: "Encryption at rest and in transit, single sign-on and audit logs come switched on from day one.",
icon: "shield",
},
{
title: "Quick off the mark",
copy: "Edge delivery and smart caching keep every screen under a hundred milliseconds worldwide.",
icon: "bolt",
},
{
title: "Clear at a glance",
copy: "Dashboards translate raw events into the two or three numbers your team actually acts on.",
icon: "chart",
},
];
const PATHS: Record<SpotItem["icon"], string> = {
shield: "M12 2 4 6v6c0 4 3 7 8 10 5-3 8-6 8-10V6z",
bolt: "M13 2 3 14h7l-1 8 10-12h-7z",
chart: "M4 20V10M10 20V4M16 20v-7M22 20H2",
};
function SpotlightCard({ item, index }: { item: SpotItem; index: number }) {
const ref = useRef<HTMLElement>(null);
const [hover, setHover] = useState(false);
const mx = useMotionValue(0);
const my = useMotionValue(0);
const fill = useMotionTemplate`radial-gradient(240px circle at ${mx}px ${my}px, rgba(99,102,241,0.20), transparent 72%)`;
const edge = useMotionTemplate`radial-gradient(220px circle at ${mx}px ${my}px, rgba(129,140,248,0.85), transparent 65%)`;
function move(e: ReactPointerEvent<HTMLElement>) {
const el = ref.current;
if (!el) return;
const rect = el.getBoundingClientRect();
mx.set(e.clientX - rect.left);
my.set(e.clientY - rect.top);
}
return (
<motion.article
ref={ref}
onPointerMove={move}
onPointerEnter={() => setHover(true)}
onPointerLeave={() => setHover(false)}
initial={{ opacity: 0, y: 40 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-60px" }}
transition={{ duration: 0.6, delay: index * 0.12, ease: EASE }}
className="group relative rounded-2xl"
>
{/* glowing border that tracks the cursor */}
<motion.div
aria-hidden="true"
style={{ background: edge, opacity: hover ? 1 : 0 }}
className="pointer-events-none absolute inset-0 rounded-2xl transition-opacity duration-300"
/>
<div className="relative m-px overflow-hidden rounded-[15px] border border-zinc-200 bg-white p-7 dark:border-zinc-800 dark:bg-zinc-900">
{/* soft interior spotlight */}
<motion.div
aria-hidden="true"
style={{ background: fill, opacity: hover ? 1 : 0 }}
className="pointer-events-none absolute inset-0 transition-opacity duration-300"
/>
{/* shimmer hairline along the top */}
<div
aria-hidden="true"
className="csf-shimmer pointer-events-none absolute inset-x-0 top-0 h-px"
/>
<div className="relative">
<span className="inline-flex h-12 w-12 items-center justify-center rounded-xl bg-indigo-50 text-indigo-600 ring-1 ring-indigo-100 transition-transform duration-300 group-hover:-translate-y-0.5 dark:bg-indigo-500/10 dark:text-indigo-300 dark:ring-indigo-500/20">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.7}
strokeLinecap="round"
strokeLinejoin="round"
className="h-6 w-6"
aria-hidden="true"
>
<path d={PATHS[item.icon]} />
</svg>
</span>
<h3 className="mt-5 text-lg font-bold text-zinc-900 dark:text-white">
{item.title}
</h3>
<p className="mt-2 text-sm leading-relaxed text-zinc-600 dark:text-zinc-400">
{item.copy}
</p>
<a
href="#"
className="mt-5 inline-flex items-center gap-1.5 text-sm font-semibold text-indigo-600 transition-colors hover:text-indigo-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:text-indigo-300 dark:focus-visible:ring-offset-zinc-900"
>
Learn more
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
className="h-4 w-4 transition-transform duration-300 group-hover:translate-x-1"
aria-hidden="true"
>
<path d="M5 12h14M13 6l6 6-6 6" />
</svg>
</a>
</div>
</div>
</motion.article>
);
}
export default function CardSpotlightFollow() {
return (
<section className="bg-white px-6 py-20 dark:bg-zinc-950 md:py-28">
<style>{`
@keyframes csf-shimmer {
0% { background-position: -150% 0; }
100% { background-position: 250% 0; }
}
.csf-shimmer {
background-image: linear-gradient(90deg, transparent, rgba(129,140,248,0.9), transparent);
background-size: 60% 100%;
background-repeat: no-repeat;
animation: csf-shimmer 3.4s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
.csf-shimmer { animation: none; opacity: 0.4; }
}
`}</style>
<div className="mx-auto max-w-6xl">
<div className="mx-auto max-w-2xl text-center">
<motion.h2
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6, ease: EASE }}
className="text-balance text-3xl font-bold tracking-tight text-zinc-900 sm:text-4xl dark:text-white"
>
A spotlight that chases your cursor
</motion.h2>
<motion.p
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: 0.1, ease: EASE }}
className="mx-auto mt-4 max-w-lg text-zinc-600 dark:text-zinc-400"
>
Hover any card and a soft light plus a glowing border follow the
pointer, drawing attention exactly where you are looking.
</motion.p>
</div>
<div className="mt-14 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{ITEMS.map((item, i) => (
<SpotlightCard key={item.title} item={item} index={i} />
))}
</div>
</div>
</section>
);
}Dependencies
Licence
Built by Web Innoventix. Free for personal and commercial use, no attribution required.
Built by Web Innoventix
Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.
Get a free quoteSimilar components
Browse all →
3D Tilt Cards
OriginalA responsive grid of cards that tilt in 3D toward your cursor with a light glare and lifted depth layers, powered by Framer Motion springs.

3D Flip Cards
OriginalPricing-style cards that flip in 3D on hover or tap to reveal details on the back, keyboard accessible with a staggered entrance animation.

Animated Gradient Border Cards
OriginalCards wrapped in a live conic gradient border that rotates and speeds up on hover, with a shimmer sweep and a scrolling tag marquee.

Glow Effect Card
primitivesA reusable glow effect card for React and Tailwind, with hover and motion.

Neon Gradient Card
gradient-card.tsxA reusable neon gradient card for React and Tailwind, with hover and motion.

Spotlight Magic Card
card.tsxA reusable spotlight magic card for React and Tailwind, with hover and motion.

Tilt Spotlight Card
primitivesA reusable tilt spotlight card for React and Tailwind, with hover and motion.

Spotlight Hero
OriginalA centred hero with a soft radial spotlight, badge and dual call-to-action.

Split Hero
OriginalA two-column hero pairing a headline and CTAs with a product mock and social proof.

Gradient Spotlight Hero
OriginalA minimal centred hero with a soft gradient-mesh backdrop, announcement pill and dual call-to-action buttons.

App Preview Hero
OriginalA centred hero that pairs headline copy with a realistic product dashboard mock built entirely from markup, complete with browser chrome and a floating notification card.

Waitlist Capture Hero
OriginalA dark, focused hero with an inline email capture form and avatar social proof, ready for pre-launch waitlists.

