Glassmorphic Pricing Card
A premium glass pricing card with smoother hover physics, metadata chips, and a sharpened blade-style spotlight for the featured tier.
Preview
Live interactive preview.
Installation
Add this component to your project using the CLI:
npx -y vui-registry-cli-v1@latest add glassmorphic-pricing-cardSource Code
"use client";
import React, { useRef } from "react";
import { motion, useMotionValue, useSpring, useTransform } from "framer-motion";
import { ArrowRight, Check } from "lucide-react";
import { cn } from "@/lib/utils";
import { ReactIcon, NextjsIcon, TailwindCssIcon as TailwindIcon } from "@/components/icons/tech-svgs";
const pricingPlans = [
{
name: "React Starter",
icon: ReactIcon,
price: "$19",
description: "Perfect for indie hackers and small React projects.",
features: [
"Access to 50+ basic components",
"Community support",
"Personal project license",
"Basic templates"
],
popular: false
},
{
name: "Next.js Pro",
icon: NextjsIcon,
price: "$49",
description: "Everything you need to build Next.js apps at lightspeed.",
features: [
"Access to all 150+ components",
"Figma design files included",
"Commercial project license",
"Priority email support",
"Free updates for 1 year"
],
popular: true
},
{
name: "Tailwind Team",
icon: TailwindIcon,
price: "$99",
description: "For agencies and teams building scalable UIs.",
features: [
"Everything in Pro",
"Unlimited team members",
"Custom component requests",
"Dedicated Slack channel",
"Lifetime updates"
],
popular: false
}
];
function PricingCard({ plan, index }: { plan: typeof pricingPlans[0], index: number }) {
const containerRef = useRef<HTMLDivElement>(null);
const mouseX = useMotionValue(0);
const mouseY = useMotionValue(0);
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
if (!containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const x = e.clientX - rect.left - rect.width / 2;
const y = e.clientY - rect.top - rect.height / 2;
mouseX.set(x);
mouseY.set(y);
};
const handleMouseLeave = () => {
mouseX.set(0);
mouseY.set(0);
};
const springX = useSpring(mouseX, { stiffness: 100, damping: 20 });
const springY = useSpring(mouseY, { stiffness: 100, damping: 20 });
const shineX = useTransform(springX, [-200, 200], [100, -100]);
const shineY = useTransform(springY, [-200, 200], [100, -100]);
return (
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
ref={containerRef}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
className={cn(
"relative w-full max-w-[340px] rounded-2xl p-[1px] group cursor-pointer overflow-hidden transition-all duration-700 ease-out",
plan.popular ? "z-10" : "z-0"
)}
>
{/* Blade-sharp Border Glow */}
<div className={cn(
"absolute inset-0 rounded-2xl bg-gradient-to-b opacity-100 transition-opacity duration-700 ease-out pointer-events-none",
plan.popular ? "from-foreground/80 via-foreground/5 to-transparent" : "from-foreground/30 via-transparent to-transparent group-hover:from-foreground/60"
)} />
{/* Glow effect matching mouse */}
<motion.div
className="absolute inset-0 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-700 ease-out z-0 pointer-events-none mix-blend-screen"
style={{
background: useTransform(
() => `radial-gradient(400px circle at calc(50% + ${shineX.get()}px) calc(50% + ${shineY.get()}px), rgba(255,255,255,0.4), transparent 40%)`
)
}}
/>
<div className="relative h-full w-full rounded-[15px] bg-background border border-foreground/5 p-8 flex flex-col overflow-hidden z-10 shadow-sm group-hover:shadow-md transition-shadow">
{/* Glass reflections */}
<div className="absolute inset-0 rounded-[15px] bg-gradient-to-tr from-foreground/[0.02] via-transparent to-foreground/[0.02] pointer-events-none" />
{plan.popular && (
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[80%] h-[2px] bg-gradient-to-r from-transparent via-foreground/60 to-transparent pointer-events-none blur-[0.5px]" />
)}
{plan.popular && (
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[40%] h-[1px] bg-gradient-to-r from-transparent via-foreground to-transparent pointer-events-none mix-blend-screen" />
)}
{/* Content */}
<div className="relative z-20 flex-1 flex flex-col">
<div className="flex justify-between items-center mb-6">
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-foreground/[0.03] border border-foreground/10 text-xs font-medium text-foreground/80">
<plan.icon className="w-3.5 h-3.5 text-foreground/70" />
{plan.name}
</div>
{plan.popular && <span className="text-[10px] font-bold tracking-widest text-foreground uppercase">Popular</span>}
</div>
<h3 className="text-4xl font-semibold text-foreground mb-2 tracking-tight">
{plan.price}<span className="text-xl text-muted-foreground font-normal">/mo</span>
</h3>
<p className="text-sm text-muted-foreground mb-8 leading-relaxed min-h-[40px]">
{plan.description}
</p>
<div className="space-y-4 mb-10 flex-1">
{plan.features.map((feature, i) => (
<div key={i} className="flex items-center gap-3">
<div className="w-5 h-5 rounded-full flex items-center justify-center shrink-0 bg-foreground/5">
<Check className="w-3 h-3 text-foreground/70" />
</div>
<span className="text-sm text-foreground/80">{feature}</span>
</div>
))}
</div>
<button className={cn(
"relative w-full h-12 rounded-lg font-semibold text-sm flex items-center justify-center gap-2 group/btn overflow-hidden transition-all",
plan.popular ? "bg-foreground text-background hover:opacity-90" : "bg-foreground/5 text-foreground hover:bg-foreground/10"
)}>
<span className="relative z-10 flex items-center gap-2">
Get Started <ArrowRight className="w-4 h-4 transition-transform group-hover/btn:translate-x-1" />
</span>
</button>
</div>
</div>
</motion.div>
)
}
export default function GlassmorphicPricing() {
return (
<div className="min-h-[800px] w-full flex flex-col items-center justify-center bg-background p-4 sm:p-8 font-sans overflow-hidden relative">
<div className="relative z-10 text-center mb-16 max-w-2xl mx-auto">
<h2 className="text-4xl md:text-5xl font-semibold tracking-tight text-foreground mb-4">
Simple, transparent pricing
</h2>
<p className="text-lg text-muted-foreground">
Choose the perfect plan for your needs. No hidden fees.
</p>
</div>
<div className="relative z-20 flex flex-col lg:flex-row items-center justify-center gap-6 lg:gap-8 w-full max-w-7xl perspective-1000">
{pricingPlans.map((plan, index) => (
<PricingCard key={plan.name} plan={plan} index={index} />
))}
</div>
</div>
);
}
Dependencies
framer-motion: latestlucide-react: latest
Props
Component property reference.
| Name | Type | Default | Description |
|---|---|---|---|
| plan | Object | - | The plan configuration including name, price, features, and popularity. |
| index | number | 0 | Index of the card for staggered entrance animations. |
Most components here are inspired by outstanding libraries and creators in the ecosystem. I don’t claim to be the original author — this is my space for learning, rebuilding, and understanding great work at a deeper level.
I’m still a student of the craft, constantly studying the best and translating what I learn through my own perspective. Every piece reflects curiosity, respect for the community, and small creative touches that feel true to me.
I’ve done my best to credit inspirations properly. If anything is missing or inaccurate, I truly appreciate a message so it can be corrected with care.