← PricingOpen in v0
Pricing 01 — Three Tiers
Three pricing cards with a highlighted middle tier, primary glow, token-tinted check rows and staggered reveals.
$ npx shadcn@latest add @ludusvibe/pricing-01Code
Installed to components/sections/pricing-01.tsx — it's yours to edit.
"use client"
import * as React from "react"
import { motion } from "motion/react"
import { Check } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import { buttonVariants } from "@/components/ui/button"
import { Reveal } from "@/components/ludus/reveal"
import { fadeUp, stagger, viewport } from "@/lib/motion"
import { cn } from "@/lib/utils"
type Cta = { label: string; href: string }
export type Pricing01Tier = {
name: string
price: string
/** Suffix rendered after the price, e.g. "/month". Omit for custom pricing. */
period?: string
description: string
features: string[]
cta: Cta
/** Visually emphasizes this tier with a badge, primary border and glow. */
highlighted?: boolean
}
export type Pricing01Props = {
eyebrow?: string
title?: React.ReactNode
description?: string
/** Badge label shown on the highlighted tier. */
highlightLabel?: string
tiers?: Pricing01Tier[]
/** Heading element — use "h1" when this is the page's main heading. */
as?: "h1" | "h2"
className?: string
}
const defaultTiers: Pricing01Tier[] = [
{
name: "Starter",
price: "$19",
period: "/month",
description: "For solo builders shipping their first product.",
features: [
"Up to 3 projects",
"10k monthly page views",
"Core analytics dashboard",
"Email capture forms",
"Community support",
],
cta: { label: "Start with Starter", href: "#" },
},
{
name: "Pro",
price: "$49",
period: "/month",
description: "For growing teams that need room to move fast.",
features: [
"Unlimited projects",
"250k monthly page views",
"Advanced funnels & cohorts",
"A/B testing built in",
"Custom domains & SSL",
"5 team seats",
"Priority support",
],
cta: { label: "Get started with Pro", href: "#" },
highlighted: true,
},
{
name: "Enterprise",
price: "Custom",
description: "For organizations with security and scale requirements.",
features: [
"Everything in Pro",
"Unlimited page views",
"SSO / SAML & audit logs",
"99.99% uptime SLA",
"Unlimited team seats",
"Dedicated success manager",
],
cta: { label: "Talk to sales", href: "#" },
},
]
export function Pricing01({
eyebrow = "Pricing",
title = "Simple pricing that scales with you",
description = "Start free, upgrade when you outgrow it. Every plan includes unlimited visitors on your published pages — no surprise overages.",
highlightLabel = "Most popular",
tiers = defaultTiers,
as: Heading = "h2",
className,
}: Pricing01Props) {
return (
<section className={cn("bg-background px-6 py-24 md:py-32", className)}>
<div className="mx-auto max-w-6xl">
<div className="mx-auto max-w-2xl text-center">
<Reveal>
<p className="text-sm font-medium tracking-widest text-primary uppercase">
{eyebrow}
</p>
</Reveal>
<Reveal delay={0.1}>
<Heading className="mt-4 text-balance font-heading text-4xl font-semibold tracking-tight md:text-5xl">
{title}
</Heading>
</Reveal>
<Reveal delay={0.2}>
<p className="mt-5 text-pretty text-muted-foreground md:text-lg">
{description}
</p>
</Reveal>
</div>
<motion.div
initial="hidden"
whileInView="visible"
viewport={viewport}
variants={stagger(0.12)}
className="mt-14 grid gap-6 md:mt-16 md:grid-cols-3"
>
{tiers.map((tier) => (
<motion.div key={tier.name} variants={fadeUp} className="relative">
{tier.highlighted && (
<div
aria-hidden
className="absolute -inset-4 rounded-[2rem] bg-primary/15 blur-2xl"
/>
)}
<div
className={cn(
"relative flex h-full flex-col rounded-3xl border bg-card p-8",
tier.highlighted && "border-primary/50 shadow-lg"
)}
>
{tier.highlighted && (
<Badge className="absolute -top-3 left-1/2 -translate-x-1/2 rounded-full px-3">
{highlightLabel}
</Badge>
)}
<h3 className="font-heading text-lg font-semibold tracking-tight">
{tier.name}
</h3>
<p className="mt-2 text-sm text-pretty text-muted-foreground">
{tier.description}
</p>
<div className="mt-6 flex items-baseline gap-1.5">
<span className="font-heading text-5xl font-semibold tracking-tighter">
{tier.price}
</span>
{tier.period && (
<span className="text-sm text-muted-foreground">
{tier.period}
</span>
)}
</div>
<ul className="mt-8 mb-8 flex flex-col gap-3">
{tier.features.map((feature) => (
<li key={feature} className="flex items-start gap-3">
<span className="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full bg-primary/10">
<Check className="size-3 text-primary" />
</span>
<span className="text-sm text-muted-foreground">
{feature}
</span>
</li>
))}
</ul>
<a
href={tier.cta.href}
className={cn(
buttonVariants({
size: "lg",
variant: tier.highlighted ? "default" : "outline",
}),
"mt-auto w-full"
)}
>
{tier.cta.label}
</a>
</div>
</motion.div>
))}
</motion.div>
</div>
</section>
)
}