Gradient Hover Card
Original · freeA card that reveals a gradient wash and lifts 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/card-gradient-hover.json"use client";
import { useState, type ReactNode } from "react";
import { motion, useReducedMotion } from "motion/react";
type Feature = {
id: string;
title: string;
blurb: string;
gradient: string;
ring: string;
icon: ReactNode;
};
type Post = {
id: string;
img: string;
category: string;
title: string;
read: string;
author: string;
gradient: string;
};
const features: Feature[] = [
{
id: "edge",
title: "Edge Functions",
blurb: "Deploy TypeScript to 300+ regions. Cold starts under 5ms, billed per request.",
gradient: "from-indigo-500/25 via-violet-500/15 to-transparent",
ring: "group-hover:ring-indigo-400/50 dark:group-hover:ring-indigo-400/40",
icon: (
<path
d="M13 2 3 14h7l-1 8 10-12h-7l1-8Z"
strokeWidth="1.6"
strokeLinecap="round"
strokeLinejoin="round"
/>
),
},
{
id: "vault",
title: "Secrets Vault",
blurb: "Encrypted env vars with per-branch scoping and automatic key rotation.",
gradient: "from-emerald-500/25 via-teal-500/15 to-transparent",
ring: "group-hover:ring-emerald-400/50 dark:group-hover:ring-emerald-400/40",
icon: (
<>
<rect x="5" y="11" width="14" height="10" rx="2" strokeWidth="1.6" />
<path d="M8 11V8a4 4 0 0 1 8 0v3" strokeWidth="1.6" strokeLinecap="round" />
</>
),
},
{
id: "insights",
title: "Live Insights",
blurb: "Real-time traces, p99 latency and error budgets streamed straight to your dashboard.",
gradient: "from-rose-500/25 via-amber-500/15 to-transparent",
ring: "group-hover:ring-rose-400/50 dark:group-hover:ring-rose-400/40",
icon: (
<>
<path d="M4 19V5" strokeWidth="1.6" strokeLinecap="round" />
<path d="M4 19h16" strokeWidth="1.6" strokeLinecap="round" />
<path d="M8 15l3-4 3 2 4-6" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
</>
),
},
];
const post: Post = {
id: "post",
img: "/img/gallery/g14.webp",
category: "Engineering",
title: "How we cut our build times from 9 minutes to 40 seconds",
read: "7 min read",
author: "Priya Nair",
gradient: "from-sky-500/30 via-indigo-500/20 to-transparent",
};
export default function CardGradientHover() {
const reduceMotion = useReducedMotion();
const [liked, setLiked] = useState(false);
const [likes, setLikes] = useState(248);
const toggleLike = () => {
setLiked((prev) => {
setLikes((n) => (prev ? n - 1 : n + 1));
return !prev;
});
};
return (
<section className="relative w-full bg-gradient-to-b from-white to-slate-50 px-5 py-20 dark:from-zinc-950 dark:to-zinc-900 sm:px-8 md:py-28">
<style>{`
@keyframes cghShine {
0% { transform: translateX(-120%) skewX(-12deg); opacity: 0; }
40% { opacity: 0.75; }
100% { transform: translateX(240%) skewX(-12deg); opacity: 0; }
}
@keyframes cghFloat {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-4px); }
}
@media (prefers-reduced-motion: reduce) {
.cgh-shine, .cgh-float { animation: none !important; }
}
`}</style>
<div className="mx-auto max-w-6xl">
<header className="mx-auto mb-14 max-w-2xl text-center">
<span className="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white/60 px-3 py-1 text-xs font-medium text-slate-600 backdrop-blur dark:border-zinc-800 dark:bg-zinc-900/60 dark:text-zinc-400">
<span className="h-1.5 w-1.5 rounded-full bg-emerald-500" />
Gradient hover cards
</span>
<h2 className="mt-5 text-balance text-3xl font-semibold tracking-tight text-slate-900 dark:text-zinc-50 sm:text-4xl">
Reveal a wash of color on hover
</h2>
<p className="mt-4 text-pretty text-base leading-relaxed text-slate-600 dark:text-zinc-400">
Each card lifts, sharpens its border and blooms a soft gradient as your pointer arrives. Tactile, quiet, and fully keyboard-accessible.
</p>
</header>
{/* Feature cards */}
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{features.map((f, i) => (
<motion.article
key={f.id}
initial={reduceMotion ? false : { opacity: 0, y: 16 }}
whileInView={reduceMotion ? undefined : { opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-60px" }}
transition={{ duration: 0.5, delay: i * 0.08, ease: [0.22, 1, 0.36, 1] }}
className="group relative overflow-hidden rounded-2xl border border-slate-200 bg-white p-6 shadow-sm ring-1 ring-transparent transition-all duration-300 ease-out hover:-translate-y-1.5 hover:shadow-xl hover:shadow-slate-900/5 dark:border-zinc-800 dark:bg-zinc-900 dark:shadow-none dark:hover:shadow-black/40 sm:p-7"
>
{/* gradient wash */}
<div
aria-hidden
className={`pointer-events-none absolute inset-0 bg-gradient-to-br ${f.gradient} opacity-0 transition-opacity duration-500 ease-out group-hover:opacity-100`}
/>
{/* shine sweep */}
<div
aria-hidden
className="pointer-events-none absolute inset-0 overflow-hidden opacity-0 transition-opacity duration-300 group-hover:opacity-100"
>
<div className="cgh-shine absolute inset-y-0 -left-1/3 w-1/3 bg-gradient-to-r from-transparent via-white/40 to-transparent group-hover:[animation:cghShine_1.1s_ease-out]" />
</div>
<div className="relative">
<span
className={`inline-flex h-11 w-11 items-center justify-center rounded-xl bg-slate-100 text-slate-700 ring-1 ring-inset ring-slate-200 transition-colors duration-300 group-hover:bg-white/70 dark:bg-zinc-800 dark:text-zinc-200 dark:ring-zinc-700 dark:group-hover:bg-zinc-800/70`}
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
className="cgh-float h-5 w-5 group-hover:[animation:cghFloat_2.4s_ease-in-out_infinite]"
aria-hidden
>
{f.icon}
</svg>
</span>
<h3 className="mt-5 text-lg font-semibold text-slate-900 dark:text-zinc-50">
{f.title}
</h3>
<p className="mt-2 text-sm leading-relaxed text-slate-600 dark:text-zinc-400">
{f.blurb}
</p>
<button
type="button"
className="mt-5 inline-flex items-center gap-1.5 rounded-lg text-sm font-medium text-slate-700 underline-offset-4 transition-colors hover:text-slate-950 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:text-zinc-300 dark:hover:text-white dark:focus-visible:ring-zinc-500 dark:focus-visible:ring-offset-zinc-900"
>
Read the docs
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" className="h-4 w-4 transition-transform duration-300 group-hover:translate-x-0.5" aria-hidden>
<path d="M5 12h14M13 6l6 6-6 6" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</button>
</div>
{/* border glow on hover via ring */}
<div
aria-hidden
className={`pointer-events-none absolute inset-0 rounded-2xl ring-1 ring-inset ring-transparent transition-[box-shadow,--tw-ring-color] duration-300 ${f.ring}`}
/>
</motion.article>
))}
</div>
{/* Featured photo card */}
<motion.article
initial={reduceMotion ? false : { opacity: 0, y: 20 }}
whileInView={reduceMotion ? undefined : { opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-60px" }}
transition={{ duration: 0.55, ease: [0.22, 1, 0.36, 1] }}
className="group relative mt-6 grid grid-cols-1 overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm transition-all duration-300 ease-out hover:-translate-y-1.5 hover:shadow-xl hover:shadow-slate-900/5 dark:border-zinc-800 dark:bg-zinc-900 dark:hover:shadow-black/40 md:grid-cols-2"
>
<div
aria-hidden
className={`pointer-events-none absolute inset-0 z-10 bg-gradient-to-tr ${post.gradient} opacity-0 transition-opacity duration-500 ease-out group-hover:opacity-100`}
/>
<div className="relative overflow-hidden">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={post.img}
alt="Developers reviewing a build pipeline dashboard on a large monitor"
loading="lazy"
draggable={false}
className="h-56 w-full object-cover transition-transform duration-500 ease-out group-hover:scale-[1.04] md:h-full"
/>
<div aria-hidden className="absolute inset-0 bg-gradient-to-t from-black/30 to-transparent md:bg-gradient-to-r" />
</div>
<div className="relative z-20 flex flex-col p-6 sm:p-8">
<div className="flex items-center gap-3">
<span className="inline-flex items-center rounded-full bg-indigo-50 px-2.5 py-0.5 text-xs font-medium text-indigo-700 ring-1 ring-inset ring-indigo-200 dark:bg-indigo-500/10 dark:text-indigo-300 dark:ring-indigo-500/20">
{post.category}
</span>
<span className="text-xs text-slate-500 dark:text-zinc-500">{post.read}</span>
</div>
<h3 className="mt-4 text-balance text-xl font-semibold leading-snug tracking-tight text-slate-900 dark:text-zinc-50 sm:text-2xl">
{post.title}
</h3>
<p className="mt-3 text-sm leading-relaxed text-slate-600 dark:text-zinc-400">
We swapped Webpack for Turbopack, sharded the test suite across the edge, and cached node_modules by lockfile hash. Here is the full teardown.
</p>
<div className="mt-auto flex items-center justify-between pt-6">
<div className="flex items-center gap-3">
<span className="grid h-9 w-9 place-items-center rounded-full bg-gradient-to-br from-indigo-500 to-violet-600 text-sm font-semibold text-white">
PN
</span>
<div className="leading-tight">
<p className="text-sm font-medium text-slate-800 dark:text-zinc-200">{post.author}</p>
<p className="text-xs text-slate-500 dark:text-zinc-500">Staff Engineer</p>
</div>
</div>
<button
type="button"
onClick={toggleLike}
aria-pressed={liked}
aria-label={liked ? "Remove like" : "Like this article"}
className="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white/70 px-3 py-1.5 text-sm font-medium text-slate-700 backdrop-blur transition-all hover:border-rose-300 hover:text-rose-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rose-400 focus-visible:ring-offset-2 focus-visible:ring-offset-white active:scale-95 dark:border-zinc-700 dark:bg-zinc-800/70 dark:text-zinc-300 dark:hover:border-rose-500/40 dark:hover:text-rose-400 dark:focus-visible:ring-offset-zinc-900"
>
<svg
viewBox="0 0 24 24"
className={`h-4 w-4 transition-transform duration-200 ${liked ? "scale-110 fill-rose-500 stroke-rose-500" : "fill-none stroke-current"}`}
strokeWidth="1.7"
aria-hidden
>
<path d="M12 20s-7-4.35-9.5-8.5C.9 8.7 2.2 5.5 5.3 5.1c1.9-.25 3.4.9 4.7 2.3 1.3-1.4 2.8-2.55 4.7-2.3 3.1.4 4.4 3.6 2.8 6.4C19 15.65 12 20 12 20Z" strokeLinejoin="round" />
</svg>
{likes}
</button>
</div>
</div>
</motion.article>
</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.

Spotlight Follow Cards
OriginalFeature 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.

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.

Profile Card
OriginalA user profile card with avatar, stats and a follow toggle.

Product Card
OriginalA product card with image, price and add-to-cart feedback.

Pricing Single Card
OriginalA single pricing plan card with feature list and CTA.

Stat Card
OriginalA metric card with value, trend arrow and a small sparkline.

