Skip to content
All Taste components

Animated sections

Pricing with billing toggle

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

Simple plans that grow with you

Solo

₹399₹319/ month
Billed monthly, cancel any timeBilled yearly, two months free

For one person billing a handful of clients.

Choose Solo
  • 50 invoices a month
  • Reminders
  • Card and bank payments

Studio

Most popular
₹799₹639/ month
Billed monthly, cancel any timeBilled yearly, two months free

For small teams who invoice every week.

Choose Studio
  • Unlimited invoices
  • Recurring schedules
  • Bank feeds
  • 3 teammates

Agency

₹1,999₹1,599/ month
Billed monthly, cancel any timeBilled yearly, two months free

For agencies with many clients and approvals.

Choose Agency
  • Everything in Studio
  • Approval flows
  • Unlimited teammates
"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.