Icon Bounce Hover Effect
Original · freeIcons that bounce or spin playfully 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-icon-bounce.json"use client";
import { useState, type ReactNode } from "react";
type PlayfulIcon = {
key: string;
label: string;
caption: string;
motion: "bounce" | "spin" | "wobble" | "pop" | "swing";
accent: string;
svg: ReactNode;
};
const ICONS: PlayfulIcon[] = [
{
key: "heart",
label: "Like",
caption: "Give it some love",
motion: "bounce",
accent: "rose",
svg: (
<path
d="M12 20.5S3.5 14.9 3.5 9.3A4.3 4.3 0 0 1 12 7.4a4.3 4.3 0 0 1 8.5 1.9c0 5.6-8.5 11.2-8.5 11.2Z"
fill="currentColor"
stroke="currentColor"
strokeWidth="1.5"
strokeLinejoin="round"
/>
),
},
{
key: "star",
label: "Favorite",
caption: "Mark as favorite",
motion: "spin",
accent: "amber",
svg: (
<path
d="m12 3.5 2.6 5.3 5.9.9-4.3 4.1 1 5.8L12 17l-5.2 2.7 1-5.8-4.3-4.1 5.9-.9L12 3.5Z"
fill="currentColor"
stroke="currentColor"
strokeWidth="1.4"
strokeLinejoin="round"
/>
),
},
{
key: "bell",
label: "Notify",
caption: "Turn on alerts",
motion: "swing",
accent: "indigo",
svg: (
<>
<path
d="M18 8.5a6 6 0 0 0-12 0c0 6-2 7.5-2 7.5h16s-2-1.5-2-7.5Z"
fill="currentColor"
stroke="currentColor"
strokeWidth="1.5"
strokeLinejoin="round"
/>
<path
d="M10.3 19.5a2 2 0 0 0 3.4 0"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</>
),
},
{
key: "bolt",
label: "Boost",
caption: "Supercharge it",
motion: "pop",
accent: "violet",
svg: (
<path
d="M13 2.5 4.5 13.2h6L11 21.5 19.5 10.8h-6L13 2.5Z"
fill="currentColor"
stroke="currentColor"
strokeWidth="1.4"
strokeLinejoin="round"
/>
),
},
{
key: "rocket",
label: "Launch",
caption: "Ship it now",
motion: "bounce",
accent: "sky",
svg: (
<>
<path
d="M12 3c3.5 1.5 5.5 5 5.5 8.5L12 15l-5.5-3.5C6.5 8 8.5 4.5 12 3Z"
fill="currentColor"
stroke="currentColor"
strokeWidth="1.4"
strokeLinejoin="round"
/>
<path
d="M9.4 15.2 7 20.5l4-2 4 2-2.4-5.3"
fill="none"
stroke="currentColor"
strokeWidth="1.4"
strokeLinejoin="round"
strokeLinecap="round"
/>
<circle cx="12" cy="9.5" r="1.4" fill="currentColor" />
</>
),
},
{
key: "smile",
label: "React",
caption: "Drop a reaction",
motion: "wobble",
accent: "emerald",
svg: (
<>
<circle cx="12" cy="12" r="8.5" fill="currentColor" opacity="0.18" />
<circle
cx="12"
cy="12"
r="8.5"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
/>
<circle cx="9.2" cy="10.5" r="1.1" fill="currentColor" />
<circle cx="14.8" cy="10.5" r="1.1" fill="currentColor" />
<path
d="M8.5 14a4.5 4.5 0 0 0 7 0"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</>
),
},
];
const ACCENTS: Record<
string,
{ text: string; ring: string; glow: string; chip: string }
> = {
rose: {
text: "text-rose-500 dark:text-rose-400",
ring: "focus-visible:ring-rose-400/70",
glow: "group-hover:shadow-rose-400/40 dark:group-hover:shadow-rose-500/30",
chip: "bg-rose-50 text-rose-700 dark:bg-rose-500/10 dark:text-rose-300",
},
amber: {
text: "text-amber-500 dark:text-amber-400",
ring: "focus-visible:ring-amber-400/70",
glow: "group-hover:shadow-amber-400/40 dark:group-hover:shadow-amber-500/30",
chip: "bg-amber-50 text-amber-700 dark:bg-amber-500/10 dark:text-amber-300",
},
indigo: {
text: "text-indigo-500 dark:text-indigo-400",
ring: "focus-visible:ring-indigo-400/70",
glow: "group-hover:shadow-indigo-400/40 dark:group-hover:shadow-indigo-500/30",
chip: "bg-indigo-50 text-indigo-700 dark:bg-indigo-500/10 dark:text-indigo-300",
},
violet: {
text: "text-violet-500 dark:text-violet-400",
ring: "focus-visible:ring-violet-400/70",
glow: "group-hover:shadow-violet-400/40 dark:group-hover:shadow-violet-500/30",
chip: "bg-violet-50 text-violet-700 dark:bg-violet-500/10 dark:text-violet-300",
},
sky: {
text: "text-sky-500 dark:text-sky-400",
ring: "focus-visible:ring-sky-400/70",
glow: "group-hover:shadow-sky-400/40 dark:group-hover:shadow-sky-500/30",
chip: "bg-sky-50 text-sky-700 dark:bg-sky-500/10 dark:text-sky-300",
},
emerald: {
text: "text-emerald-500 dark:text-emerald-400",
ring: "focus-visible:ring-emerald-400/70",
glow: "group-hover:shadow-emerald-400/40 dark:group-hover:shadow-emerald-500/30",
chip: "bg-emerald-50 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-300",
},
};
export default function HoverIconBounce() {
const [tapped, setTapped] = useState<string | null>(null);
return (
<section className="relative w-full overflow-hidden bg-slate-50 px-6 py-20 text-slate-900 dark:bg-slate-950 dark:text-slate-100 sm:px-8 md:py-28">
<style>{`
@keyframes hib-bounce {
0%, 100% { transform: translateY(0) scale(1); }
30% { transform: translateY(-9px) scale(1.12); }
55% { transform: translateY(2px) scale(0.96); }
75% { transform: translateY(-3px) scale(1.03); }
}
@keyframes hib-spin {
0% { transform: rotate(0deg) scale(1); }
60% { transform: rotate(345deg) scale(1.12); }
100% { transform: rotate(360deg) scale(1); }
}
@keyframes hib-wobble {
0%, 100% { transform: rotate(0deg); }
20% { transform: rotate(-13deg) scale(1.08); }
40% { transform: rotate(10deg) scale(1.08); }
60% { transform: rotate(-6deg); }
80% { transform: rotate(3deg); }
}
@keyframes hib-pop {
0% { transform: scale(1); }
40% { transform: scale(1.28); }
70% { transform: scale(0.92); }
100% { transform: scale(1.06); }
}
@keyframes hib-swing {
0%, 100% { transform: rotate(0deg); transform-origin: top center; }
20% { transform: rotate(16deg); transform-origin: top center; }
45% { transform: rotate(-12deg); transform-origin: top center; }
70% { transform: rotate(7deg); transform-origin: top center; }
88% { transform: rotate(-3deg); transform-origin: top center; }
}
@keyframes hib-tap {
0% { transform: scale(1); }
45% { transform: scale(1.35); }
100% { transform: scale(1); }
}
@keyframes hib-float-in {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.hib-card { will-change: transform; }
.hib-icon { will-change: transform; }
.hib-card:hover .hib-icon[data-motion="bounce"],
.hib-card:focus-within .hib-icon[data-motion="bounce"] {
animation: hib-bounce 0.7s cubic-bezier(0.28, 0.84, 0.42, 1) both;
}
.hib-card:hover .hib-icon[data-motion="spin"],
.hib-card:focus-within .hib-icon[data-motion="spin"] {
animation: hib-spin 0.7s cubic-bezier(0.34, 1.4, 0.5, 1) both;
}
.hib-card:hover .hib-icon[data-motion="wobble"],
.hib-card:focus-within .hib-icon[data-motion="wobble"] {
animation: hib-wobble 0.7s ease-in-out both;
}
.hib-card:hover .hib-icon[data-motion="pop"],
.hib-card:focus-within .hib-icon[data-motion="pop"] {
animation: hib-pop 0.55s cubic-bezier(0.34, 1.56, 0.5, 1) both;
}
.hib-card:hover .hib-icon[data-motion="swing"],
.hib-card:focus-within .hib-icon[data-motion="swing"] {
animation: hib-swing 0.8s ease-in-out both;
}
.hib-icon[data-tapped="true"] {
animation: hib-tap 0.4s cubic-bezier(0.34, 1.56, 0.5, 1) both !important;
}
@media (prefers-reduced-motion: reduce) {
.hib-card:hover .hib-icon,
.hib-card:focus-within .hib-icon,
.hib-icon[data-tapped="true"] {
animation: none !important;
}
.hib-card:hover .hib-icon,
.hib-card:focus-within .hib-icon {
transform: scale(1.08);
}
.hib-float { animation: none !important; }
}
`}</style>
<div className="pointer-events-none absolute -left-24 top-0 h-72 w-72 rounded-full bg-indigo-300/20 blur-3xl dark:bg-indigo-500/10" />
<div className="pointer-events-none absolute -right-16 bottom-0 h-72 w-72 rounded-full bg-rose-300/20 blur-3xl dark:bg-rose-500/10" />
<div className="relative mx-auto max-w-5xl">
<div className="hib-float mx-auto max-w-2xl text-center" style={{ animation: "hib-float-in 0.5s ease-out both" }}>
<span className="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white px-3 py-1 text-xs font-medium tracking-wide text-slate-600 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-400">
<span className="h-1.5 w-1.5 rounded-full bg-emerald-500" />
Hover · Interactive
</span>
<h2 className="mt-5 text-3xl font-semibold tracking-tight sm:text-4xl">
Icons with a little bounce
</h2>
<p className="mt-3 text-base leading-relaxed text-slate-600 dark:text-slate-400">
Hover, tab, or tap each button. Every icon has its own playful
reaction — a bounce, a spin, a swing — and it all goes still if you
prefer reduced motion.
</p>
</div>
<ul className="mt-12 grid grid-cols-2 gap-4 sm:grid-cols-3 sm:gap-5">
{ICONS.map((icon) => {
const accent = ACCENTS[icon.accent];
const isTapped = tapped === icon.key;
return (
<li key={icon.key}>
<button
type="button"
aria-label={`${icon.label} — ${icon.caption}`}
onClick={() => {
setTapped(icon.key);
window.setTimeout(() => {
setTapped((cur) => (cur === icon.key ? null : cur));
}, 450);
}}
className={`hib-card group flex w-full flex-col items-center gap-3 rounded-2xl border border-slate-200 bg-white px-4 py-7 text-center shadow-sm outline-none transition-all duration-300 hover:-translate-y-1 hover:border-slate-300 hover:shadow-lg focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-50 dark:border-slate-800 dark:bg-slate-900 dark:hover:border-slate-700 dark:focus-visible:ring-offset-slate-950 ${accent.glow} ${accent.ring}`}
>
<span
className={`flex h-14 w-14 items-center justify-center rounded-2xl ${accent.chip}`}
>
<span
className={`hib-icon ${accent.text}`}
data-motion={icon.motion}
data-tapped={isTapped ? "true" : "false"}
>
<svg
viewBox="0 0 24 24"
width="30"
height="30"
aria-hidden="true"
focusable="false"
>
{icon.svg}
</svg>
</span>
</span>
<span className="text-sm font-semibold text-slate-900 dark:text-slate-100">
{icon.label}
</span>
<span className="text-xs leading-snug text-slate-500 dark:text-slate-400">
{icon.caption}
</span>
</button>
</li>
);
})}
</ul>
<p className="mt-8 text-center text-xs text-slate-400 dark:text-slate-500">
Keyboard-friendly: press Tab to focus a tile and watch it react.
</p>
</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 Glow Hover Effect
OriginalA card with a coloured glow that blooms 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.

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

