Card Glow Hover Effect
Original · freeA card with a coloured glow that blooms on hover.
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/hover-card-glow.json"use client";
import { useState, useRef, useId } from "react";
type Glow = "indigo" | "violet" | "emerald" | "rose" | "amber" | "sky";
type Card = {
id: string;
glow: Glow;
eyebrow: string;
title: string;
body: string;
meta: string;
icon: "spark" | "shield" | "pulse";
};
const CARDS: Card[] = [
{
id: "latency",
glow: "indigo",
eyebrow: "Edge network",
title: "Sub-40ms cold starts",
body: "Functions boot at the closest region to your visitor, so the first byte lands before the spinner ever shows.",
meta: "312 regions live",
icon: "spark",
},
{
id: "guardrails",
glow: "emerald",
eyebrow: "Zero-config safety",
title: "Rollbacks that never lose data",
body: "Every deploy is atomic. If a release misbehaves, one click restores the exact prior state — schema and all.",
meta: "1-click revert",
icon: "shield",
},
{
id: "signals",
glow: "rose",
eyebrow: "Live telemetry",
title: "See every request in flight",
body: "Trace a slow call from the browser down to the query that caused it, streamed in real time with no sampling.",
meta: "0% sampling",
icon: "pulse",
},
];
const GLOW: Record<
Glow,
{ bloom: string; ring: string; text: string; dot: string; chip: string }
> = {
indigo: {
bloom:
"from-indigo-400/40 via-indigo-500/20 dark:from-indigo-500/45 dark:via-indigo-400/25",
ring: "group-hover:border-indigo-300/70 group-focus-visible:border-indigo-300/70 dark:group-hover:border-indigo-400/50 dark:group-focus-visible:border-indigo-400/50",
text: "text-indigo-600 dark:text-indigo-300",
dot: "bg-indigo-500 dark:bg-indigo-400",
chip: "bg-indigo-50 text-indigo-700 ring-indigo-200 dark:bg-indigo-500/10 dark:text-indigo-300 dark:ring-indigo-400/20",
},
violet: {
bloom:
"from-violet-400/40 via-violet-500/20 dark:from-violet-500/45 dark:via-violet-400/25",
ring: "group-hover:border-violet-300/70 group-focus-visible:border-violet-300/70 dark:group-hover:border-violet-400/50 dark:group-focus-visible:border-violet-400/50",
text: "text-violet-600 dark:text-violet-300",
dot: "bg-violet-500 dark:bg-violet-400",
chip: "bg-violet-50 text-violet-700 ring-violet-200 dark:bg-violet-500/10 dark:text-violet-300 dark:ring-violet-400/20",
},
emerald: {
bloom:
"from-emerald-400/40 via-emerald-500/20 dark:from-emerald-500/45 dark:via-emerald-400/25",
ring: "group-hover:border-emerald-300/70 group-focus-visible:border-emerald-300/70 dark:group-hover:border-emerald-400/50 dark:group-focus-visible:border-emerald-400/50",
text: "text-emerald-600 dark:text-emerald-300",
dot: "bg-emerald-500 dark:bg-emerald-400",
chip: "bg-emerald-50 text-emerald-700 ring-emerald-200 dark:bg-emerald-500/10 dark:text-emerald-300 dark:ring-emerald-400/20",
},
rose: {
bloom:
"from-rose-400/40 via-rose-500/20 dark:from-rose-500/45 dark:via-rose-400/25",
ring: "group-hover:border-rose-300/70 group-focus-visible:border-rose-300/70 dark:group-hover:border-rose-400/50 dark:group-focus-visible:border-rose-400/50",
text: "text-rose-600 dark:text-rose-300",
dot: "bg-rose-500 dark:bg-rose-400",
chip: "bg-rose-50 text-rose-700 ring-rose-200 dark:bg-rose-500/10 dark:text-rose-300 dark:ring-rose-400/20",
},
amber: {
bloom:
"from-amber-400/40 via-amber-500/20 dark:from-amber-500/45 dark:via-amber-400/25",
ring: "group-hover:border-amber-300/70 group-focus-visible:border-amber-300/70 dark:group-hover:border-amber-400/50 dark:group-focus-visible:border-amber-400/50",
text: "text-amber-600 dark:text-amber-300",
dot: "bg-amber-500 dark:bg-amber-400",
chip: "bg-amber-50 text-amber-700 ring-amber-200 dark:bg-amber-500/10 dark:text-amber-300 dark:ring-amber-400/20",
},
sky: {
bloom:
"from-sky-400/40 via-sky-500/20 dark:from-sky-500/45 dark:via-sky-400/25",
ring: "group-hover:border-sky-300/70 group-focus-visible:border-sky-300/70 dark:group-hover:border-sky-400/50 dark:group-focus-visible:border-sky-400/50",
text: "text-sky-600 dark:text-sky-300",
dot: "bg-sky-500 dark:bg-sky-400",
chip: "bg-sky-50 text-sky-700 ring-sky-200 dark:bg-sky-500/10 dark:text-sky-300 dark:ring-sky-400/20",
},
};
function Icon({ name, className }: { name: Card["icon"]; className?: string }) {
if (name === "shield") {
return (
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.75}
strokeLinecap="round"
strokeLinejoin="round"
className={className}
aria-hidden="true"
>
<path d="M12 3l7 3v5c0 4.5-3 7.5-7 9-4-1.5-7-4.5-7-9V6l7-3z" />
<path d="M9 12l2 2 4-4" />
</svg>
);
}
if (name === "pulse") {
return (
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.75}
strokeLinecap="round"
strokeLinejoin="round"
className={className}
aria-hidden="true"
>
<path d="M3 12h4l2 6 4-14 2 8h6" />
</svg>
);
}
return (
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.75}
strokeLinecap="round"
strokeLinejoin="round"
className={className}
aria-hidden="true"
>
<path d="M12 3v4M12 17v4M3 12h4M17 12h4M6 6l2.5 2.5M15.5 15.5L18 18M18 6l-2.5 2.5M8.5 15.5L6 18" />
<circle cx="12" cy="12" r="2.5" />
</svg>
);
}
function Arrow({ className }: { className?: string }) {
return (
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
className={className}
aria-hidden="true"
>
<path d="M5 12h14M13 6l6 6-6 6" />
</svg>
);
}
export default function HoverCardGlow() {
const [active, setActive] = useState<string | null>(null);
const gridRef = useRef<HTMLUListElement>(null);
const uid = useId().replace(/[:]/g, "");
const prefix = `hcg-${uid}`;
return (
<section className="relative w-full overflow-hidden bg-slate-50 px-6 py-20 sm:py-28 dark:bg-slate-950">
<style>{`
@keyframes ${prefix}-drift {
0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
50% { transform: translate3d(0, -6px, 0) scale(1.04); }
}
@keyframes ${prefix}-rise {
from { opacity: 0; transform: translateY(14px); }
to { opacity: 1; transform: translateY(0); }
}
.${prefix}-rise { animation: ${prefix}-rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) both; }
.${prefix}-drift { animation: ${prefix}-drift 6s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
.${prefix}-rise, .${prefix}-drift { animation: none !important; }
}
`}</style>
<div className="relative mx-auto max-w-6xl">
<div className={`${prefix}-rise mx-auto mb-14 max-w-2xl text-center`}>
<span className="inline-flex items-center gap-2 rounded-full bg-white px-3 py-1 text-xs font-medium text-slate-600 ring-1 ring-slate-200 dark:bg-slate-900 dark:text-slate-300 dark:ring-slate-800">
<span className="relative flex h-2 w-2">
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-70" />
<span className="relative inline-flex h-2 w-2 rounded-full bg-emerald-500" />
</span>
Platform, shipped
</span>
<h2 className="mt-5 text-3xl font-semibold tracking-tight text-slate-900 sm:text-4xl dark:text-white">
Everything glows when it works
</h2>
<p className="mt-4 text-base leading-relaxed text-slate-600 dark:text-slate-400">
Hover or focus any card to see the surface come alive. Built for
keyboards, screen readers, and reduced-motion the same day it was
built for the mouse.
</p>
</div>
<ul
ref={gridRef}
className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3"
>
{CARDS.map((card, i) => {
const g = GLOW[card.glow];
const isActive = active === card.id;
return (
<li
key={card.id}
className={`${prefix}-rise`}
style={{ animationDelay: `${120 + i * 90}ms` }}
>
<a
href="#"
aria-label={`${card.title} — learn more`}
onMouseEnter={() => setActive(card.id)}
onMouseLeave={() => setActive((p) => (p === card.id ? null : p))}
onFocus={() => setActive(card.id)}
onBlur={() => setActive((p) => (p === card.id ? null : p))}
className={`group relative flex h-full flex-col overflow-hidden rounded-3xl border border-slate-200 bg-white p-6 outline-none transition-[transform,box-shadow,border-color] duration-500 ease-out hover:-translate-y-1.5 focus-visible:-translate-y-1.5 focus-visible:ring-2 focus-visible:ring-slate-400 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-50 dark:border-slate-800 dark:bg-slate-900 dark:focus-visible:ring-slate-500 dark:focus-visible:ring-offset-slate-950 ${g.ring}`}
>
{/* Blooming glow */}
<span
aria-hidden="true"
className={`pointer-events-none absolute -inset-px z-0 rounded-3xl bg-gradient-to-br to-transparent opacity-0 blur-2xl transition-opacity duration-500 ease-out group-hover:opacity-100 group-focus-visible:opacity-100 ${g.bloom} ${
isActive ? `${prefix}-drift` : ""
}`}
/>
{/* Sheen line */}
<span
aria-hidden="true"
className="pointer-events-none absolute inset-x-6 top-0 z-10 h-px bg-gradient-to-r from-transparent via-slate-300/80 to-transparent opacity-0 transition-opacity duration-500 group-hover:opacity-100 group-focus-visible:opacity-100 dark:via-white/30"
/>
<div className="relative z-10 flex flex-1 flex-col">
<div className="flex items-center justify-between">
<span
className={`flex h-11 w-11 items-center justify-center rounded-2xl ring-1 transition-transform duration-500 group-hover:scale-110 group-focus-visible:scale-110 ${g.chip} ${g.text}`}
>
<Icon name={card.icon} className="h-5 w-5" />
</span>
<span
className={`inline-flex items-center gap-1.5 text-xs font-medium ${g.text}`}
>
<span className={`h-1.5 w-1.5 rounded-full ${g.dot}`} />
{card.meta}
</span>
</div>
<p
className={`mt-6 text-xs font-semibold uppercase tracking-wider ${g.text}`}
>
{card.eyebrow}
</p>
<h3 className="mt-1.5 text-lg font-semibold tracking-tight text-slate-900 dark:text-white">
{card.title}
</h3>
<p className="mt-2.5 flex-1 text-sm leading-relaxed text-slate-600 dark:text-slate-400">
{card.body}
</p>
<span className="mt-6 inline-flex items-center gap-1.5 text-sm font-medium text-slate-500 transition-colors duration-300 dark:text-slate-400">
<span
className={`transition-colors duration-300 ${
isActive ? g.text : ""
}`}
>
Explore
</span>
<Arrow className="h-4 w-4 transition-transform duration-300 group-hover:translate-x-1 group-focus-visible:translate-x-1" />
</span>
</div>
</a>
</li>
);
})}
</ul>
</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 →
Card Lift Hover Effect
OriginalA card that lifts with a growing shadow on hover.

Card Border Draw Hover Effect
OriginalA card whose border draws itself in on hover.

Card Gradient Shift Hover Effect
OriginalA card whose gradient background shifts on hover.

Card Tilt Hover Effect
OriginalA card that tilts in 3D toward the cursor on hover.

Card Spotlight Hover Effect
OriginalA card with a cursor-following radial spotlight.

Link Underline Hover Effect
OriginalAnimated link underline effects: grow, slide and centre-out.

Link Slide Hover Effect
OriginalLinks whose label slides up to a duplicate on hover.

Button Fill Hover Effect
OriginalButtons with a colour fill that sweeps across on hover.

Button Shine Hover Effect
OriginalButtons with a diagonal shine sweep on hover.
Icon Fill Hover Effect
OriginalIcon buttons whose icon and background fill on hover.
Icon Bounce Hover Effect
OriginalIcons that bounce or spin playfully on hover.

List Highlight Hover Effect
OriginalA list where the hovered row gets a sliding highlight and arrow.

