Web InnoventixFreeCode

Minimal Pricing

Original · free

minimal pricing tiers

byWeb InnoventixReact + Tailwind
pricexminimalpricing
Open

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-minimal.json
pricex-minimal.tsx
"use client";

import { useState } from "react";
import { motion, AnimatePresence, useReducedMotion } from "motion/react";

type Billing = "monthly" | "annually";

interface Tier {
  id: string;
  name: string;
  tagline: string;
  priceMonthly: number;
  priceAnnually: number;
  cta: string;
  featured: boolean;
  features: string[];
}

const TIERS: Tier[] = [
  {
    id: "starter",
    name: "Starter",
    tagline: "For individuals shaping their first workflow.",
    priceMonthly: 0,
    priceAnnually: 0,
    cta: "Start for free",
    featured: false,
    features: [
      "Up to 3 active projects",
      "1 workspace member",
      "7-day activity history",
      "Core board & list views",
      "Community support",
    ],
  },
  {
    id: "pro",
    name: "Pro",
    tagline: "For small teams that ship every week.",
    priceMonthly: 19,
    priceAnnually: 15,
    cta: "Start 14-day trial",
    featured: true,
    features: [
      "Unlimited projects",
      "Up to 10 members",
      "Unlimited activity history",
      "Custom fields, views & filters",
      "Priority email support",
    ],
  },
  {
    id: "scale",
    name: "Scale",
    tagline: "For orgs that need control and uptime.",
    priceMonthly: 49,
    priceAnnually: 39,
    cta: "Contact sales",
    featured: false,
    features: [
      "Everything in Pro",
      "Unlimited members",
      "SSO, SAML & audit logs",
      "Granular roles & permissions",
      "Dedicated success manager",
    ],
  },
];

function CheckIcon({ className }: { className?: string }) {
  return (
    <svg
      viewBox="0 0 20 20"
      fill="none"
      aria-hidden="true"
      className={className}
    >
      <path
        d="M4.5 10.5l3.5 3.5 7.5-8"
        stroke="currentColor"
        strokeWidth="2"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

function SparkIcon({ className }: { className?: string }) {
  return (
    <svg viewBox="0 0 20 20" fill="none" aria-hidden="true" className={className}>
      <path
        d="M10 2.5l1.6 4.4 4.4 1.6-4.4 1.6L10 14.5l-1.6-4.4L4 8.5l4.4-1.6L10 2.5z"
        fill="currentColor"
      />
    </svg>
  );
}

export default function PricexMinimal() {
  const shouldReduce = useReducedMotion();
  const [billing, setBilling] = useState<Billing>("monthly");

  const options: { id: Billing; label: string }[] = [
    { id: "monthly", label: "Monthly" },
    { id: "annually", label: "Annually" },
  ];

  return (
    <section className="pxmin-root relative w-full overflow-hidden bg-neutral-50 px-5 py-24 text-neutral-900 sm:px-6 sm:py-28 dark:bg-zinc-950 dark:text-zinc-50">
      <style>{`
        @keyframes pxmin-rise {
          from { opacity: 0; transform: translateY(14px); }
          to { opacity: 1; transform: none; }
        }
        @keyframes pxmin-float {
          0%, 100% { transform: translate3d(0, 0, 0) scale(1); opacity: 0.55; }
          50% { transform: translate3d(0, -18px, 0) scale(1.05); opacity: 0.8; }
        }
        .pxmin-glow {
          animation: pxmin-float 9s ease-in-out infinite;
        }
        @media (prefers-reduced-motion: reduce) {
          .pxmin-root *, .pxmin-root *::before, .pxmin-root *::after {
            animation: none !important;
            transition: none !important;
          }
        }
      `}</style>

      {/* decorative background */}
      <div aria-hidden="true" className="pointer-events-none absolute inset-0 overflow-hidden">
        <div className="pxmin-glow absolute left-1/2 top-[-6rem] h-72 w-72 -translate-x-1/2 rounded-full bg-indigo-300/40 blur-3xl dark:bg-indigo-600/20" />
        <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(255,255,255,0.05)_1px,transparent_0)]" />
      </div>

      <div className="relative mx-auto max-w-6xl">
        {/* header */}
        <div className="mx-auto max-w-2xl text-center">
          <span className="inline-flex items-center gap-1.5 rounded-full border border-indigo-200 bg-indigo-50 px-3 py-1 text-xs font-medium uppercase tracking-wider text-indigo-700 dark:border-indigo-500/30 dark:bg-indigo-500/10 dark:text-indigo-300">
            <SparkIcon className="h-3.5 w-3.5" />
            Pricing
          </span>
          <h2 className="mt-5 text-balance text-4xl font-semibold tracking-tight sm:text-5xl">
            Simple pricing that scales with you
          </h2>
          <p className="mt-4 text-pretty text-base leading-relaxed text-neutral-600 dark:text-zinc-400">
            Start free and upgrade the moment your team outgrows it. No seat games,
            no surprise line items. Switch or cancel whenever you like.
          </p>
        </div>

        {/* billing toggle */}
        <div className="mt-9 flex items-center justify-center gap-3">
          <div
            role="radiogroup"
            aria-label="Billing period"
            className="relative inline-flex rounded-full border border-neutral-200 bg-white p-1 shadow-sm dark:border-zinc-800 dark:bg-zinc-900"
          >
            <span
              aria-hidden="true"
              className="absolute inset-y-1 left-1 w-[calc(50%-0.25rem)] rounded-full bg-neutral-900 shadow transition-transform duration-300 ease-out dark:bg-zinc-50"
              style={{
                transform:
                  billing === "annually" ? "translateX(100%)" : "translateX(0)",
              }}
            />
            {options.map((opt) => {
              const active = billing === opt.id;
              return (
                <button
                  key={opt.id}
                  type="button"
                  role="radio"
                  aria-checked={active}
                  onClick={() => setBilling(opt.id)}
                  className={`relative z-10 rounded-full px-5 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-zinc-900 ${
                    active
                      ? "text-white dark:text-zinc-900"
                      : "text-neutral-600 hover:text-neutral-900 dark:text-zinc-400 dark:hover:text-zinc-100"
                  }`}
                >
                  {opt.label}
                </button>
              );
            })}
          </div>
          <span className="hidden rounded-full bg-emerald-100 px-2.5 py-1 text-xs font-medium text-emerald-700 sm:inline-block dark:bg-emerald-500/15 dark:text-emerald-300">
            Save 20%
          </span>
        </div>

        {/* tiers */}
        <div className="mt-14 grid grid-cols-1 gap-6 md:grid-cols-3 md:items-stretch">
          {TIERS.map((tier, i) => {
            const price =
              billing === "monthly" ? tier.priceMonthly : tier.priceAnnually;
            const isFree = tier.priceMonthly === 0;

            return (
              <motion.div
                key={tier.id}
                initial={shouldReduce ? false : { opacity: 0, y: 16 }}
                whileInView={{ opacity: 1, y: 0 }}
                viewport={{ once: true, amount: 0.3 }}
                transition={{
                  duration: shouldReduce ? 0 : 0.45,
                  delay: shouldReduce ? 0 : i * 0.08,
                  ease: "easeOut",
                }}
                className={`relative flex flex-col rounded-2xl p-7 ${
                  tier.featured
                    ? "border-2 border-indigo-500 bg-white shadow-xl shadow-indigo-500/10 md:-mt-4 md:mb-4 dark:border-indigo-500 dark:bg-zinc-900"
                    : "border border-neutral-200 bg-white/70 dark:border-zinc-800 dark:bg-zinc-900/60"
                }`}
              >
                {tier.featured && (
                  <span className="absolute -top-3 left-1/2 inline-flex -translate-x-1/2 items-center gap-1 rounded-full bg-indigo-500 px-3 py-1 text-xs font-semibold text-white shadow-md">
                    <SparkIcon className="h-3 w-3" />
                    Most popular
                  </span>
                )}

                <div className="flex items-baseline justify-between">
                  <h3 className="text-lg font-semibold tracking-tight">
                    {tier.name}
                  </h3>
                </div>
                <p className="mt-1.5 min-h-[2.5rem] text-sm leading-relaxed text-neutral-600 dark:text-zinc-400">
                  {tier.tagline}
                </p>

                <div className="mt-6 flex items-end gap-1">
                  <span className="text-4xl font-semibold tracking-tight tabular-nums">
                    <AnimatePresence mode="popLayout" initial={false}>
                      <motion.span
                        key={billing}
                        initial={shouldReduce ? false : { opacity: 0, y: 8 }}
                        animate={{ opacity: 1, y: 0 }}
                        exit={
                          shouldReduce ? { opacity: 0 } : { opacity: 0, y: -8 }
                        }
                        transition={{ duration: shouldReduce ? 0 : 0.22 }}
                        className="inline-block"
                      >
                        ${price}
                      </motion.span>
                    </AnimatePresence>
                  </span>
                  <span className="pb-1 text-sm text-neutral-500 dark:text-zinc-500">
                    {isFree ? "forever" : "/ mo"}
                  </span>
                </div>
                <p className="mt-1 h-5 text-xs text-neutral-500 dark:text-zinc-500">
                  {isFree
                    ? "No credit card required"
                    : billing === "annually"
                      ? `Billed annually · $${price * 12}/yr`
                      : "Billed monthly"}
                </p>

                <button
                  type="button"
                  className={`mt-6 w-full rounded-xl px-4 py-2.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-zinc-900 ${
                    tier.featured
                      ? "bg-indigo-500 text-white hover:bg-indigo-600"
                      : "border border-neutral-300 bg-white text-neutral-900 hover:bg-neutral-100 dark:border-zinc-700 dark:bg-zinc-800/60 dark:text-zinc-50 dark:hover:bg-zinc-800"
                  }`}
                >
                  {tier.cta}
                </button>

                <ul className="mt-7 space-y-3.5 border-t border-neutral-200/80 pt-6 dark:border-zinc-800">
                  {tier.features.map((feature) => (
                    <li key={feature} className="flex items-start gap-3 text-sm">
                      <span
                        className={`mt-0.5 flex h-5 w-5 flex-none items-center justify-center rounded-full ${
                          tier.featured
                            ? "bg-indigo-100 text-indigo-600 dark:bg-indigo-500/20 dark:text-indigo-300"
                            : "bg-emerald-100 text-emerald-600 dark:bg-emerald-500/15 dark:text-emerald-400"
                        }`}
                      >
                        <CheckIcon className="h-3.5 w-3.5" />
                      </span>
                      <span className="text-neutral-700 dark:text-zinc-300">
                        {feature}
                      </span>
                    </li>
                  ))}
                </ul>
              </motion.div>
            );
          })}
        </div>

        <p className="mt-12 text-center text-sm text-neutral-500 dark:text-zinc-500">
          All plans include SSL, unlimited guests, and a 30-day money-back
          guarantee.
        </p>
      </div>
    </section>
  );
}

Dependencies

motion

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 quote

Similar components

Browse all →
Simple Pricing

Simple Pricing

Original

A three-tier pricing section with a highlighted popular plan.

Single Plan Pricing Card

Single Plan Pricing Card

Original

A 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

Three Tier Pricing With Monthly Annual Toggle

Original

A 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

Plan Feature Comparison Table

Original

A 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

Staggered Glow Pricing Tiers

Original

A 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

Spotlight Cursor Pricing Cards

Original

Pricing 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

Highlight Follow Pricing Plans

Original

A 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

Three Tier Pricing

Original

three-tier pricing cards

Toggle Annual Pricing

Toggle Annual Pricing

Original

pricing with monthly/annual toggle

Single Highlight Pricing

Single Highlight Pricing

Original

single highlighted plan

Comparison Table Pricing

Comparison Table Pricing

Original

pricing comparison table

Cards Gradient Pricing

Cards Gradient Pricing

Original

gradient pricing cards