Glass Button
Original · freeGlassmorphic buttons over a gradient backdrop.
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/btn-glass.json"use client";
import { useState, type ButtonHTMLAttributes, type ReactNode } from "react";
import { useReducedMotion } from "motion/react";
type Variant = "frost" | "tint" | "outline";
type Size = "sm" | "md" | "lg";
const sizeMap: Record<Size, string> = {
sm: "h-9 px-4 text-sm gap-1.5 rounded-xl",
md: "h-11 px-5 text-sm gap-2 rounded-2xl",
lg: "h-13 px-7 text-base gap-2.5 rounded-2xl",
};
const base =
"relative inline-flex select-none items-center justify-center font-medium tracking-tight " +
"backdrop-blur-xl transition-[transform,box-shadow,background-color,border-color] duration-200 " +
"will-change-transform active:scale-[0.97] " +
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70 focus-visible:ring-offset-2 " +
"focus-visible:ring-offset-transparent disabled:pointer-events-none disabled:opacity-45 " +
"border shadow-[0_8px_30px_-12px_rgba(0,0,0,0.5)]";
const variantMap: Record<Variant, string> = {
frost:
"bg-white/15 border-white/30 text-white hover:bg-white/25 " +
"shadow-[inset_0_1px_0_0_rgba(255,255,255,0.35),0_8px_30px_-12px_rgba(0,0,0,0.5)]",
tint:
"bg-indigo-400/25 border-indigo-200/40 text-white hover:bg-indigo-400/35 " +
"shadow-[inset_0_1px_0_0_rgba(255,255,255,0.3),0_10px_34px_-14px_rgba(79,70,229,0.7)]",
outline:
"bg-white/5 border-white/40 text-white hover:bg-white/15 " +
"shadow-[inset_0_1px_0_0_rgba(255,255,255,0.2),0_6px_24px_-14px_rgba(0,0,0,0.5)]",
};
function GlassButton({
children,
variant = "frost",
size = "md",
className = "",
...rest
}: {
children: ReactNode;
variant?: Variant;
size?: Size;
className?: string;
} & ButtonHTMLAttributes<HTMLButtonElement>) {
return (
<button
className={`btnglass-sheen ${base} ${variantMap[variant]} ${sizeMap[size]} ${className}`}
{...rest}
>
{children}
</button>
);
}
function IconArrow() {
return (
<svg
viewBox="0 0 24 24"
className="h-4 w-4"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="M5 12h14" />
<path d="m13 6 6 6-6 6" />
</svg>
);
}
function IconSpinner() {
return (
<svg viewBox="0 0 24 24" className="btnglass-spin h-4 w-4" fill="none" aria-hidden="true">
<circle cx="12" cy="12" r="9" stroke="currentColor" strokeOpacity="0.3" strokeWidth="2.5" />
<path
d="M21 12a9 9 0 0 0-9-9"
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
/>
</svg>
);
}
function IconHeart({ filled }: { filled: boolean }) {
return (
<svg
viewBox="0 0 24 24"
className="h-4 w-4"
fill={filled ? "currentColor" : "none"}
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.6l-1-1a5.5 5.5 0 1 0-7.8 7.8l1 1L12 21l7.8-7.6 1-1a5.5 5.5 0 0 0 0-7.8Z" />
</svg>
);
}
function IconCheck() {
return (
<svg
viewBox="0 0 24 24"
className="h-4 w-4"
fill="none"
stroke="currentColor"
strokeWidth={2.5}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="m20 6-11 11-5-5" />
</svg>
);
}
function IconCopy() {
return (
<svg
viewBox="0 0 24 24"
className="h-4 w-4"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<rect x="9" y="9" width="11" height="11" rx="2.5" />
<path d="M5 15V6a2 2 0 0 1 2-2h9" />
</svg>
);
}
function IconGoogle() {
return (
<svg viewBox="0 0 24 24" className="h-4 w-4" aria-hidden="true">
<path
fill="#EA4335"
d="M12 10.2v3.9h5.5c-.24 1.5-1.68 4.4-5.5 4.4A6.5 6.5 0 1 1 16.4 6.9l2.7-2.6A10 10 0 1 0 22 12c0-.66-.07-1.2-.17-1.8H12Z"
/>
</svg>
);
}
function IconPlay() {
return (
<svg viewBox="0 0 24 24" className="h-4 w-4" fill="currentColor" aria-hidden="true">
<path d="M8 5.5v13a1 1 0 0 0 1.5.87l11-6.5a1 1 0 0 0 0-1.74l-11-6.5A1 1 0 0 0 8 5.5Z" />
</svg>
);
}
export default function BtnGlass() {
const reduce = useReducedMotion();
const blob = reduce ? "" : "btnglass-blob";
const [liked, setLiked] = useState(false);
const [likeCount, setLikeCount] = useState(248);
const [copied, setCopied] = useState(false);
const [loading, setLoading] = useState(false);
const [subscribed, setSubscribed] = useState(false);
const toggleLike = () => {
setLiked((v) => {
setLikeCount((c) => c + (v ? -1 : 1));
return !v;
});
};
const copyCode = async () => {
try {
await navigator.clipboard.writeText("INNOVENTIX25");
setCopied(true);
window.setTimeout(() => setCopied(false), 1800);
} catch {
setCopied(false);
}
};
const runLoading = () => {
if (loading) return;
setLoading(true);
window.setTimeout(() => setLoading(false), 1900);
};
return (
<section className="relative w-full overflow-hidden px-6 py-20 sm:py-24">
<style>{`
@keyframes btnglass-sheen-move {
0% { transform: translateX(-130%) skewX(-18deg); }
60%, 100% { transform: translateX(230%) skewX(-18deg); }
}
@keyframes btnglass-spin-rot {
to { transform: rotate(360deg); }
}
@keyframes btnglass-blob {
0%, 100% { transform: translate(0, 0) scale(1); }
33% { transform: translate(6%, -5%) scale(1.12); }
66% { transform: translate(-5%, 4%) scale(0.94); }
}
.btnglass-sheen { overflow: hidden; }
.btnglass-sheen::after {
content: "";
position: absolute;
top: 0; bottom: 0; left: 0;
width: 45%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.55), transparent);
transform: translateX(-130%) skewX(-18deg);
pointer-events: none;
opacity: 0;
}
.btnglass-sheen:hover::after {
opacity: 1;
animation: btnglass-sheen-move 1.1s ease-in-out;
}
.btnglass-spin { animation: btnglass-spin-rot 0.8s linear infinite; }
.btnglass-blob { animation: btnglass-blob 18s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
.btnglass-sheen:hover::after { animation: none; opacity: 0; }
.btnglass-spin { animation: none; }
.btnglass-blob { animation: none; }
}
`}</style>
{/* Gradient backdrop */}
<div className="absolute inset-0 -z-10 bg-gradient-to-br from-indigo-600 via-violet-600 to-sky-500 dark:from-indigo-900 dark:via-violet-900 dark:to-slate-950" />
<div
className={`${blob} absolute -left-24 top-8 -z-10 h-72 w-72 rounded-full bg-rose-400/40 blur-3xl dark:bg-rose-500/25`}
style={{ animationDelay: "-4s" }}
aria-hidden="true"
/>
<div
className={`${blob} absolute -right-16 bottom-0 -z-10 h-80 w-80 rounded-full bg-emerald-300/40 blur-3xl dark:bg-emerald-500/20`}
aria-hidden="true"
/>
<div
className={`${blob} absolute right-1/3 top-1/4 -z-10 h-64 w-64 rounded-full bg-amber-300/30 blur-3xl dark:bg-amber-400/15`}
style={{ animationDelay: "-9s" }}
aria-hidden="true"
/>
<div className="mx-auto max-w-3xl">
<div className="rounded-3xl border border-white/20 bg-white/10 p-8 shadow-2xl backdrop-blur-2xl sm:p-10">
<header className="mb-8">
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-white/70">
FreeCode / Buttons
</p>
<h2 className="mt-2 text-2xl font-semibold tracking-tight text-white sm:text-3xl">
Glass buttons
</h2>
<p className="mt-2 text-sm text-white/70">
Frosted, tactile controls that layer over any gradient.
</p>
</header>
{/* Variants */}
<div className="mb-8">
<p className="mb-3 text-xs font-medium uppercase tracking-wider text-white/60">
Variants
</p>
<div className="flex flex-wrap items-center gap-3">
<GlassButton variant="frost">
Get started
<IconArrow />
</GlassButton>
<GlassButton variant="tint">
<IconPlay />
Watch demo
</GlassButton>
<GlassButton variant="outline">Learn more</GlassButton>
</div>
</div>
{/* Sizes */}
<div className="mb-8">
<p className="mb-3 text-xs font-medium uppercase tracking-wider text-white/60">
Sizes
</p>
<div className="flex flex-wrap items-center gap-3">
<GlassButton variant="frost" size="sm">
Small
</GlassButton>
<GlassButton variant="frost" size="md">
Medium
</GlassButton>
<GlassButton variant="frost" size="lg">
Large
</GlassButton>
</div>
</div>
{/* Interactive */}
<div className="mb-8">
<p className="mb-3 text-xs font-medium uppercase tracking-wider text-white/60">
Interactive
</p>
<div className="flex flex-wrap items-center gap-3">
<GlassButton
variant={liked ? "tint" : "frost"}
onClick={toggleLike}
aria-pressed={liked}
aria-label={liked ? "Remove like" : "Add like"}
>
<span className={liked ? "text-rose-200" : "text-white"}>
<IconHeart filled={liked} />
</span>
{likeCount.toLocaleString()}
</GlassButton>
<GlassButton variant="frost" onClick={copyCode} aria-live="polite">
{copied ? (
<span className="text-emerald-200">
<IconCheck />
</span>
) : (
<IconCopy />
)}
{copied ? "Copied" : "INNOVENTIX25"}
</GlassButton>
<GlassButton
variant="tint"
onClick={runLoading}
disabled={loading}
aria-busy={loading}
>
{loading ? <IconSpinner /> : <IconArrow />}
{loading ? "Deploying" : "Deploy"}
</GlassButton>
</div>
</div>
{/* Toggle + social + disabled */}
<div>
<p className="mb-3 text-xs font-medium uppercase tracking-wider text-white/60">
States
</p>
<div className="flex flex-wrap items-center gap-3">
<GlassButton
variant={subscribed ? "tint" : "outline"}
onClick={() => setSubscribed((v) => !v)}
aria-pressed={subscribed}
>
{subscribed ? (
<>
<span className="text-emerald-200">
<IconCheck />
</span>
Subscribed
</>
) : (
"Subscribe"
)}
</GlassButton>
<GlassButton variant="frost">
<IconGoogle />
Sign in with Google
</GlassButton>
<GlassButton
variant="frost"
aria-label="Next"
size="md"
className="!px-0 w-11"
>
<IconArrow />
</GlassButton>
<GlassButton variant="outline" disabled>
Unavailable
</GlassButton>
</div>
</div>
</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 →
Shimmer Button
MITA dark pill button with a continuous conic-gradient light shimmer sweeping around its border.

Rainbow Gradient Button
MITA button wrapped in an animated multi-colour gradient border with a matching blurred underglow.

Interactive Hover Button
MITA pill button whose dot expands to fill the surface and reveals an arrow label on hover.

Pulsating Button
MITA solid button that emits a soft, continuously pulsing glow ring to draw the eye to the primary action.

Shiny Button
MITA frosted-glass button with a spring-animated light sweeping across its text and border.

Solid Set Button
OriginalA set of solid buttons: primary, secondary, success and danger, in three sizes.

Outline Set Button
OriginalOutline button variants across colours and sizes with a hover fill.

Ghost Set Button
OriginalGhost and text buttons with subtle hover backgrounds.

Soft Set Button
OriginalSoft, tinted buttons across semantic colours.

Gradient Set Button
OriginalGradient-filled buttons with a hover shift.
Icon Leading Button
OriginalButtons with leading and trailing icons.
Icon Only Button
OriginalSquare and circular icon-only buttons with tooltips and aria-labels.

