Solo
₹399₹319/ month
Billed monthly, cancel any timeBilled yearly, two months freeFor one person billing a handful of clients.
Choose Solo- 50 invoices a month
- Reminders
- Card and bank payments
Animated sections
Three plans with a monthly or yearly switch; the thumb springs across and each price rolls to the new amount.
monthly · yearly · sliding thumb · price roll · saving badge · featured plan
For one person billing a handful of clients.
Choose SoloFor small teams who invoice every week.
Choose StudioFor agencies with many clients and approvals.
Choose Agency"use client";
import { useState } from "react";
import { Check } from "lucide-react";
import { cn } from "@/lib/utils";
type Period = "monthly" | "yearly";
const plans = [
{ name: "Solo", monthly: "₹399", yearly: "₹319", text: "For one person billing a handful of clients.", features: ["50 invoices a month", "Reminders", "Card and bank payments"] },
{ name: "Studio", monthly: "₹799", yearly: "₹639", text: "For small teams who invoice every week.", features: ["Unlimited invoices", "Recurring schedules", "Bank feeds", "3 teammates"], featured: true },
{ name: "Agency", monthly: "₹1,999", yearly: "₹1,599", text: "For agencies with many clients and approvals.", features: ["Everything in Studio", "Approval flows", "Unlimited teammates"] },
];
/** Both values share one grid cell; the active one rolls in, the other rolls out. */
function Roll({ show, a, b, className }: { show: Period; a: string; b: string; className?: string }) {
const base = "col-start-1 row-start-1 transition-[opacity,transform,filter] duration-500 ease-spring motion-reduce:transition-none";
return (
<span className={cn("inline-grid justify-items-start", className)}>
<span aria-hidden={show !== "monthly"} className={cn(base, show === "monthly" ? "opacity-100" : "invisible -translate-y-1/2 opacity-0 blur-[4px]")}>{a}</span>
<span aria-hidden={show !== "yearly"} className={cn(base, show === "yearly" ? "opacity-100" : "invisible translate-y-1/2 opacity-0 blur-[4px]")}>{b}</span>
</span>
);
}
export function PricingToggle() {
const [period, setPeriod] = useState<Period>("monthly");
return (
<section aria-labelledby="pricing-title" className="bg-bg py-20 font-sans">
<div className="mx-auto max-w-5xl px-4 sm:px-6">
<div className="mb-8 flex flex-col items-center gap-3 text-center">
<h2 id="pricing-title" className="text-3xl font-medium tracking-tight text-fg">Simple plans that grow with you</h2>
<div role="group" aria-label="Billing period" className="relative grid grid-cols-2 rounded-full bg-surface-2 p-[3px]">
<span
aria-hidden
className={cn(
"absolute inset-y-[3px] left-[3px] w-[calc(50%-3px)] rounded-full bg-bg shadow-sm transition-transform duration-500 ease-spring motion-reduce:transition-none",
period === "yearly" && "translate-x-full",
)}
/>
{(["monthly", "yearly"] as const).map((p) => (
<button
key={p}
type="button"
aria-pressed={period === p}
onClick={() => setPeriod(p)}
className={cn("relative z-10 inline-flex h-8 items-center justify-center gap-1.5 whitespace-nowrap rounded-full px-4 text-[13px] font-medium transition-colors", period === p ? "text-fg" : "text-fg-muted")}
>
{p === "monthly" ? "Monthly" : "Yearly"}
{p === "yearly" && (
<span key={period} className="rounded-full bg-success-soft px-1.5 text-[11px] text-success-fg animate-pop motion-reduce:animate-none">−20%</span>
)}
</button>
))}
</div>
</div>
<div className="grid gap-3 md:grid-cols-3">
{plans.map((plan) => (
<article key={plan.name} className={cn("grid content-start gap-3.5 rounded-lg border bg-bg p-5", plan.featured ? "border-primary ring-1 ring-primary" : "border-border")}>
<div className="flex items-center justify-between">
<h3 className="text-[15px] font-medium text-fg">{plan.name}</h3>
{plan.featured && <span className="rounded-full bg-primary-soft px-2 py-0.5 text-xs font-medium text-primary-soft-fg">Most popular</span>}
</div>
<p className="flex items-baseline gap-1">
<Roll show={period} a={plan.monthly} b={plan.yearly} className="text-[34px] font-medium tabular-nums tracking-tight text-fg" />
<span className="text-[13px] text-fg-subtle">/ month</span>
</p>
<Roll show={period} a="Billed monthly, cancel any time" b="Billed yearly, two months free" className="text-[13px] text-fg-subtle" />
<p className="text-[13.5px] text-fg-muted">{plan.text}</p>
<a
href={"/signup?plan=" + plan.name.toLowerCase() + "&period=" + period}
className={cn("inline-flex h-9 items-center justify-center rounded text-sm font-medium transition active:scale-[.97]", plan.featured ? "bg-primary text-primary-fg hover:bg-primary-hover" : "border border-border-strong text-fg hover:bg-surface")}
>
Choose {plan.name}
</a>
<ul className="grid gap-2 text-[13px] text-fg-muted">
{plan.features.map((f) => (
<li key={f} className="flex gap-2"><Check className="mt-0.5 size-3.5 shrink-0 text-primary" aria-hidden />{f}</li>
))}
</ul>
</article>
))}
</div>
</div>
</section>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.