Two Col Pricing
Original · freetwo-column pricing
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/pricex-two-col.json"use client";
import { useId, useState } from "react";
import { motion, useReducedMotion } from "motion/react";
type BillingCycle = "monthly" | "annual";
type PlanFeature = {
label: string;
included: boolean;
};
type Plan = {
id: string;
name: string;
tagline: string;
monthly: number;
annual: number;
featured: boolean;
badge?: string;
cta: string;
features: PlanFeature[];
};
const PLANS: Plan[] = [
{
id: "starter",
name: "Solo",
tagline: "Everything one maker needs to ship and stay organized.",
monthly: 12,
annual: 9,
featured: false,
cta: "Start free trial",
features: [
{ label: "Up to 3 active projects", included: true },
{ label: "5 GB encrypted storage", included: true },
{ label: "Unlimited public share links", included: true },
{ label: "Community support (48h)", included: true },
{ label: "Version history — 7 days", included: true },
{ label: "Priority render queue", included: false },
{ label: "Team roles & permissions", included: false },
{ label: "SSO / SAML sign-in", included: false },
],
},
{
id: "team",
name: "Studio",
tagline: "For small teams that ship weekly and need real control.",
monthly: 39,
annual: 31,
featured: true,
badge: "Most picked",
cta: "Upgrade to Studio",
features: [
{ label: "Unlimited active projects", included: true },
{ label: "1 TB encrypted storage", included: true },
{ label: "Unlimited public share links", included: true },
{ label: "Priority support (4h)", included: true },
{ label: "Version history — 1 year", included: true },
{ label: "Priority render queue", included: true },
{ label: "Team roles & permissions", included: true },
{ label: "SSO / SAML sign-in", included: true },
],
},
];
function CheckIcon({ className }: { className?: string }) {
return (
<svg
viewBox="0 0 20 20"
fill="none"
stroke="currentColor"
strokeWidth={2.2}
strokeLinecap="round"
strokeLinejoin="round"
className={className}
aria-hidden="true"
>
<path d="M4 10.5l3.5 3.5L16 5.5" />
</svg>
);
}
function DashIcon({ className }: { className?: string }) {
return (
<svg
viewBox="0 0 20 20"
fill="none"
stroke="currentColor"
strokeWidth={2.2}
strokeLinecap="round"
className={className}
aria-hidden="true"
>
<path d="M5 10h10" />
</svg>
);
}
function SparkIcon({ className }: { className?: string }) {
return (
<svg viewBox="0 0 24 24" fill="none" className={className} aria-hidden="true">
<path
d="M12 2.5l1.9 5.6a3 3 0 001.9 1.9l5.6 1.9-5.6 1.9a3 3 0 00-1.9 1.9L12 21.5l-1.9-5.6a3 3 0 00-1.9-1.9L2.6 12l5.6-1.9a3 3 0 001.9-1.9L12 2.5z"
fill="currentColor"
/>
</svg>
);
}
export default function PricexTwoCol() {
const reduceMotion = useReducedMotion();
const [cycle, setCycle] = useState<BillingCycle>("annual");
const groupId = useId();
const isAnnual = cycle === "annual";
return (
<section className="relative w-full overflow-hidden bg-slate-50 px-5 py-24 text-slate-900 sm:px-8 dark:bg-slate-950 dark:text-slate-50">
<style>{`
@keyframes pricex2c-float {
0%, 100% { transform: translate3d(0, 0, 0); }
50% { transform: translate3d(0, -14px, 0); }
}
@keyframes pricex2c-drift {
0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
50% { transform: translate3d(0, 20px, 0) scale(1.06); }
}
@keyframes pricex2c-sheen {
0% { background-position: 0% 50%; }
100% { background-position: 200% 50%; }
}
.pricex2c-float { animation: pricex2c-float 9s ease-in-out infinite; }
.pricex2c-drift { animation: pricex2c-drift 13s ease-in-out infinite; }
.pricex2c-sheen {
background-size: 200% auto;
animation: pricex2c-sheen 6s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
.pricex2c-float,
.pricex2c-drift,
.pricex2c-sheen { animation: none !important; }
}
`}</style>
{/* decorative backdrop */}
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0 -z-10"
>
<div className="pricex2c-float absolute -left-24 top-10 h-72 w-72 rounded-full bg-gradient-to-br from-indigo-400/40 to-violet-500/30 blur-3xl dark:from-indigo-600/30 dark:to-violet-700/20" />
<div className="pricex2c-drift absolute -right-20 bottom-0 h-80 w-80 rounded-full bg-gradient-to-br from-sky-300/40 to-emerald-400/20 blur-3xl dark:from-sky-700/25 dark:to-emerald-800/15" />
<div className="absolute inset-0 bg-[radial-gradient(circle_at_1px_1px,rgba(15,23,42,0.05)_1px,transparent_0)] [background-size:22px_22px] dark:bg-[radial-gradient(circle_at_1px_1px,rgba(226,232,240,0.06)_1px,transparent_0)]" />
</div>
<div className="mx-auto max-w-5xl">
{/* header */}
<div className="mx-auto max-w-2xl text-center">
<span className="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white/70 px-4 py-1.5 text-xs font-semibold uppercase tracking-[0.14em] text-indigo-600 shadow-sm backdrop-blur dark:border-slate-800 dark:bg-slate-900/60 dark:text-indigo-300">
<SparkIcon className="h-3.5 w-3.5" />
Simple pricing
</span>
<h2 className="mt-6 text-balance text-4xl font-extrabold tracking-tight sm:text-5xl">
Two plans. Zero guesswork.
</h2>
<p className="mx-auto mt-4 max-w-xl text-pretty text-base leading-relaxed text-slate-600 dark:text-slate-400">
Start solo and grow into a studio. Switch, pause, or cancel any time —
no sales calls, no hidden seats, no surprise usage bills.
</p>
{/* billing toggle */}
<div
role="radiogroup"
aria-label="Billing cycle"
className="mx-auto mt-9 inline-flex items-center gap-1 rounded-full border border-slate-200 bg-white p-1 shadow-sm dark:border-slate-800 dark:bg-slate-900"
>
{(["monthly", "annual"] as const).map((option) => {
const active = cycle === option;
return (
<button
key={option}
type="button"
role="radio"
aria-checked={active}
onClick={() => setCycle(option)}
className={`relative rounded-full px-5 py-2 text-sm font-semibold outline-none transition-colors focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-slate-900 ${
active
? "text-white"
: "text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-100"
}`}
>
{active && (
<motion.span
layoutId={`${groupId}-cycle-pill`}
transition={
reduceMotion
? { duration: 0 }
: { type: "spring", stiffness: 420, damping: 34 }
}
className="absolute inset-0 -z-10 rounded-full bg-slate-900 dark:bg-indigo-500"
/>
)}
<span className="capitalize">{option}</span>
{option === "annual" && (
<span
className={`ml-2 rounded-full px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wide ${
active
? "bg-white/20 text-white"
: "bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
}`}
>
-22%
</span>
)}
</button>
);
})}
</div>
</div>
{/* plans */}
<div className="mt-14 grid gap-6 md:grid-cols-2 md:items-stretch">
{PLANS.map((plan, index) => {
const price = isAnnual ? plan.annual : plan.monthly;
return (
<motion.div
key={plan.id}
initial={reduceMotion ? false : { opacity: 0, y: 26 }}
whileInView={reduceMotion ? undefined : { opacity: 1, y: 0 }}
viewport={{ once: true, amount: 0.3 }}
transition={{
duration: 0.5,
delay: reduceMotion ? 0 : index * 0.08,
ease: [0.22, 1, 0.36, 1],
}}
className={`group relative flex flex-col rounded-3xl p-7 sm:p-8 ${
plan.featured
? "bg-slate-900 text-slate-100 shadow-2xl shadow-indigo-500/20 ring-1 ring-slate-800 dark:bg-slate-900 dark:ring-indigo-500/40"
: "bg-white text-slate-900 shadow-xl shadow-slate-900/5 ring-1 ring-slate-200 dark:bg-slate-900/50 dark:text-slate-100 dark:ring-slate-800"
}`}
>
{plan.featured && (
<>
<div
aria-hidden="true"
className="pricex2c-sheen pointer-events-none absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-indigo-400 to-transparent"
/>
{plan.badge && (
<span className="absolute -top-3 right-7 inline-flex items-center gap-1 rounded-full bg-gradient-to-r from-indigo-500 to-violet-500 px-3 py-1 text-[11px] font-bold uppercase tracking-wide text-white shadow-lg">
<SparkIcon className="h-3 w-3" />
{plan.badge}
</span>
)}
</>
)}
<div className="flex items-baseline justify-between">
<h3 className="text-lg font-bold tracking-tight">{plan.name}</h3>
<span
className={`text-xs font-semibold uppercase tracking-wider ${
plan.featured
? "text-indigo-300"
: "text-slate-400 dark:text-slate-500"
}`}
>
{isAnnual ? "billed yearly" : "billed monthly"}
</span>
</div>
<p
className={`mt-2 text-sm leading-relaxed ${
plan.featured
? "text-slate-400"
: "text-slate-500 dark:text-slate-400"
}`}
>
{plan.tagline}
</p>
<div className="mt-6 flex items-end gap-1">
<span className="text-2xl font-semibold text-slate-400 dark:text-slate-500">
$
</span>
<motion.span
key={`${plan.id}-${price}`}
initial={reduceMotion ? false : { opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, ease: "easeOut" }}
className={`text-5xl font-extrabold tracking-tight ${
plan.featured
? "pricex2c-sheen bg-gradient-to-r from-white via-indigo-200 to-white bg-clip-text text-transparent"
: ""
}`}
>
{price}
</motion.span>
<span
className={`mb-1.5 text-sm font-medium ${
plan.featured
? "text-slate-400"
: "text-slate-500 dark:text-slate-400"
}`}
>
/seat · mo
</span>
</div>
<p
className={`mt-1 h-5 text-xs ${
plan.featured
? "text-emerald-300"
: "text-emerald-600 dark:text-emerald-400"
}`}
>
{isAnnual
? `Save $${(plan.monthly - plan.annual) * 12} per seat each year`
: " "}
</p>
<a
href="#get-started"
className={`mt-6 inline-flex w-full items-center justify-center rounded-xl px-5 py-3 text-sm font-semibold outline-none transition-transform duration-200 hover:-translate-y-0.5 focus-visible:ring-2 focus-visible:ring-offset-2 ${
plan.featured
? "bg-gradient-to-r from-indigo-500 to-violet-500 text-white shadow-lg shadow-indigo-500/30 hover:shadow-indigo-500/50 focus-visible:ring-indigo-400 focus-visible:ring-offset-slate-900"
: "bg-slate-900 text-white hover:bg-slate-800 focus-visible:ring-slate-500 focus-visible:ring-offset-white dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-white dark:focus-visible:ring-offset-slate-900"
}`}
>
{plan.cta}
</a>
<ul className="mt-7 space-y-3.5">
{plan.features.map((feature) => (
<li
key={feature.label}
className={`flex items-center gap-3 text-sm ${
feature.included
? plan.featured
? "text-slate-200"
: "text-slate-700 dark:text-slate-200"
: plan.featured
? "text-slate-500 line-through"
: "text-slate-400 line-through dark:text-slate-600"
}`}
>
<span
className={`flex h-5 w-5 flex-none items-center justify-center rounded-full ${
feature.included
? plan.featured
? "bg-indigo-500/20 text-indigo-300"
: "bg-emerald-100 text-emerald-600 dark:bg-emerald-500/15 dark:text-emerald-400"
: plan.featured
? "bg-slate-800 text-slate-500"
: "bg-slate-100 text-slate-400 dark:bg-slate-800 dark:text-slate-600"
}`}
>
{feature.included ? (
<CheckIcon className="h-3.5 w-3.5" />
) : (
<DashIcon className="h-3.5 w-3.5" />
)}
</span>
{feature.label}
</li>
))}
</ul>
</motion.div>
);
})}
</div>
<p className="mt-10 text-center text-sm text-slate-500 dark:text-slate-400">
Prices in USD. 14-day free trial on every plan — no card required.{" "}
<a
href="#compare"
className="font-semibold text-indigo-600 underline-offset-4 hover:underline dark:text-indigo-400"
>
Compare all features
</a>
</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 →
Simple Pricing
OriginalA three-tier pricing section with a highlighted popular plan.

Single Plan Pricing Card
OriginalA focused one-plan pricing card with price, trial badge, feature list, call to action and a money-back trust note, ideal when you sell a single flat-rate product.

Three Tier Pricing With Monthly Annual Toggle
OriginalA three-tier pricing grid with a pure-CSS monthly and annual billing toggle that swaps every price with no JavaScript, using a native checkbox and Tailwind group-has state.

Plan Feature Comparison Table
OriginalA responsive, semantic feature comparison table across three plans with grouped rows, tick and dash indicators and screen-reader labels so visitors can weigh every feature side by side.

Staggered Glow Pricing Tiers
OriginalA three-tier pricing section whose cards reveal in a staggered spring cascade, with a rotating conic-gradient glow on the popular plan and prices that count up smoothly when you toggle monthly and annual billing.

Spotlight Cursor Pricing Cards
OriginalPricing cards that light up with a cursor-following radial spotlight, drift over floating background orbs and rise into view with a staggered spring entrance, plus a pulsing glow on the popular plan.

Highlight Follow Pricing Plans
OriginalA pricing row where an animated gradient highlight glides via shared layout animation to whichever plan you hover, a three-way billing switch slides its pill, prices recount instantly and a perks marquee scrolls beneath.

Three Tier Pricing
Originalthree-tier pricing cards

Toggle Annual Pricing
Originalpricing with monthly/annual toggle

Single Highlight Pricing
Originalsingle highlighted plan

Comparison Table Pricing
Originalpricing comparison table

Cards Gradient Pricing
Originalgradient pricing cards

