AIEO-ready websites that get cited by AITalk to us

Spotlight Cursor Pricing Cards

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.

React + Tailwindnpm: motioncursor spotlightanimated pricingframer motionhover glowmouse follow pricing
Original · free to use
npx shadcn@latest add https://webinnoventix.com/r/aprice-spotlight-cursor-cards.json
aprice-spotlight-cursor-cards.tsx
"use client";

import { type MouseEvent } from "react";
import {
  motion,
  useMotionValue,
  useMotionTemplate,
  useReducedMotion,
} from "motion/react";

type Plan = {
  name: string;
  blurb: string;
  price: string;
  period: string;
  featured: boolean;
  cta: string;
  features: string[];
};

const plans: Plan[] = [
  {
    name: "Personal",
    blurb: "Everything you need to launch on your own.",
    price: "£0",
    period: "forever",
    featured: false,
    cta: "Get started",
    features: ["1 project", "Community support", "1 GB storage", "Standard templates"],
  },
  {
    name: "Studio",
    blurb: "For growing teams that ship together.",
    price: "£38",
    period: "per month",
    featured: true,
    cta: "Start free trial",
    features: [
      "Unlimited projects",
      "Priority support",
      "500 GB storage",
      "Premium templates",
      "Shared libraries",
      "Version history",
    ],
  },
  {
    name: "Agency",
    blurb: "Advanced control for larger organisations.",
    price: "£96",
    period: "per month",
    featured: false,
    cta: "Talk to sales",
    features: ["Everything in Studio", "Unlimited storage", "SSO and SCIM", "Custom roles", "SLA"],
  },
];

const container = {
  hidden: {},
  show: { transition: { staggerChildren: 0.14, delayChildren: 0.08 } },
};

const item = {
  hidden: { opacity: 0, y: 40, scale: 0.97 },
  show: {
    opacity: 1,
    y: 0,
    scale: 1,
    transition: { type: "spring" as const, stiffness: 84, damping: 15 },
  },
};

function SpotlightCard({ plan, reduce }: { plan: Plan; reduce: boolean }) {
  const mx = useMotionValue(-200);
  const my = useMotionValue(-200);
  const spotlight = useMotionTemplate`radial-gradient(260px circle at ${mx}px ${my}px, rgba(129,140,248,0.35), transparent 72%)`;

  function handleMove(e: MouseEvent<HTMLDivElement>) {
    if (reduce) return;
    const rect = e.currentTarget.getBoundingClientRect();
    mx.set(e.clientX - rect.left);
    my.set(e.clientY - rect.top);
  }

  return (
    <motion.div
      variants={item}
      onMouseMove={handleMove}
      whileHover={reduce ? undefined : { y: -6 }}
      transition={{ type: "spring", stiffness: 300, damping: 22 }}
      className={
        "group relative flex h-full flex-col rounded-3xl border p-8 backdrop-blur-sm " +
        (plan.featured
          ? "border-indigo-400/60 bg-white/80 shadow-xl dark:border-indigo-400/40 dark:bg-white/5"
          : "border-zinc-200 bg-white/70 shadow-sm dark:border-white/10 dark:bg-white/[0.03]")
      }
    >
      {plan.featured && !reduce && (
        <motion.div
          aria-hidden="true"
          className="pointer-events-none absolute -inset-px -z-10 rounded-3xl"
          style={{ boxShadow: "0 0 0 0 rgba(99,102,241,0)" }}
          animate={{
            boxShadow: [
              "0 0 24px 0px rgba(99,102,241,0.15)",
              "0 0 48px 6px rgba(129,140,248,0.35)",
              "0 0 24px 0px rgba(99,102,241,0.15)",
            ],
          }}
          transition={{ repeat: Infinity, duration: 3.6, ease: "easeInOut" }}
        />
      )}

      <motion.div
        aria-hidden="true"
        className="pointer-events-none absolute inset-0 rounded-3xl opacity-0 transition-opacity duration-300 group-hover:opacity-100"
        style={{ background: spotlight }}
      />

      <div className="relative">
        <div className="flex items-center justify-between">
          <h3 className="text-lg font-semibold text-zinc-900 dark:text-white">{plan.name}</h3>
          {plan.featured && (
            <span className="inline-flex items-center gap-1.5 rounded-full bg-indigo-500/10 px-2.5 py-1 text-xs font-semibold text-indigo-600 dark:text-indigo-300">
              <span className="relative flex h-1.5 w-1.5">
                {!reduce && (
                  <span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-indigo-400 opacity-75" />
                )}
                <span className="relative inline-flex h-1.5 w-1.5 rounded-full bg-indigo-500" />
              </span>
              Popular
            </span>
          )}
        </div>
        <p className="mt-1 text-sm text-zinc-500 dark:text-zinc-400">{plan.blurb}</p>

        <div className="mt-6 flex items-baseline gap-1.5">
          <span className="text-5xl font-bold tracking-tight text-zinc-900 dark:text-white">
            {plan.price}
          </span>
          <span className="text-sm text-zinc-500 dark:text-zinc-400">{plan.period}</span>
        </div>

        <a
          href="#"
          className={
            "mt-6 inline-flex w-full items-center justify-center rounded-xl px-4 py-3 text-sm font-semibold transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500 " +
            (plan.featured
              ? "bg-gradient-to-r from-indigo-600 to-violet-600 text-white shadow-lg shadow-indigo-500/25 hover:from-indigo-500 hover:to-violet-500"
              : "border border-zinc-200 bg-white text-zinc-900 hover:bg-zinc-50 dark:border-white/15 dark:bg-white/5 dark:text-white dark:hover:bg-white/10")
          }
        >
          {plan.cta}
        </a>

        <ul className="mt-8 space-y-3 border-t border-zinc-100 pt-6 text-sm dark:border-white/10">
          {plan.features.map((feature) => (
            <li
              key={feature}
              className="flex items-start gap-3 text-zinc-700 dark:text-zinc-300"
            >
              <svg
                className="mt-0.5 h-5 w-5 shrink-0 text-indigo-500 dark:text-indigo-400"
                viewBox="0 0 20 20"
                fill="currentColor"
                aria-hidden="true"
              >
                <path
                  fillRule="evenodd"
                  d="M16.7 5.3a1 1 0 0 1 0 1.4l-7.5 7.5a1 1 0 0 1-1.4 0L3.3 10.7a1 1 0 1 1 1.4-1.4l3.8 3.8 6.8-6.8a1 1 0 0 1 1.4 0Z"
                  clipRule="evenodd"
                />
              </svg>
              <span>{feature}</span>
            </li>
          ))}
        </ul>
      </div>
    </motion.div>
  );
}

export default function SpotlightCursorPricing() {
  const reduce = useReducedMotion() ?? false;

  return (
    <section className="relative overflow-hidden bg-gradient-to-b from-white to-zinc-50 px-6 py-20 dark:from-zinc-950 dark:to-zinc-900 md:py-28">
      <style>{`
        @keyframes aprice-grad-pan {
          0% { background-position: 0% 50%; }
          100% { background-position: 200% 50%; }
        }
        .aprice-grad {
          background-image: linear-gradient(90deg,#6366f1,#a855f7,#ec4899,#6366f1);
          background-size: 200% auto;
          -webkit-background-clip: text;
          background-clip: text;
          color: transparent;
          animation: aprice-grad-pan 6s linear infinite;
        }
        @media (prefers-reduced-motion: reduce) {
          .aprice-grad { animation: none; }
        }
      `}</style>

      {!reduce && (
        <>
          <motion.div
            aria-hidden="true"
            className="pointer-events-none absolute -left-24 top-10 -z-0 h-72 w-72 rounded-full bg-indigo-400/25 blur-3xl dark:bg-indigo-500/20"
            animate={{ x: [0, 40, 0], y: [0, 30, 0], scale: [1, 1.15, 1] }}
            transition={{ repeat: Infinity, duration: 14, ease: "easeInOut" }}
          />
          <motion.div
            aria-hidden="true"
            className="pointer-events-none absolute -right-24 bottom-0 -z-0 h-80 w-80 rounded-full bg-fuchsia-400/20 blur-3xl dark:bg-fuchsia-500/15"
            animate={{ x: [0, -50, 0], y: [0, -30, 0], scale: [1.1, 1, 1.1] }}
            transition={{ repeat: Infinity, duration: 16, ease: "easeInOut" }}
          />
        </>
      )}

      <div className="relative mx-auto max-w-6xl">
        <motion.div
          initial={{ opacity: 0, y: 16 }}
          whileInView={{ opacity: 1, y: 0 }}
          viewport={{ once: true, amount: 0.6 }}
          transition={{ duration: 0.5 }}
          className="text-center"
        >
          <h2 className="text-3xl font-bold tracking-tight text-balance sm:text-4xl">
            <span className="text-zinc-900 dark:text-white">Plans built to </span>
            <span className="aprice-grad">move with you</span>
          </h2>
          <p className="mx-auto mt-3 max-w-lg text-zinc-600 dark:text-zinc-400">
            Move your cursor across a card to light it up. Every plan includes a fourteen day trial,
            no card required.
          </p>
        </motion.div>

        <motion.div
          variants={container}
          initial="hidden"
          whileInView="show"
          viewport={{ once: true, amount: 0.2 }}
          className="mt-14 grid gap-6 lg:grid-cols-3"
        >
          {plans.map((plan) => (
            <SpotlightCard key={plan.name} plan={plan} reduce={reduce} />
          ))}
        </motion.div>
      </div>
    </section>
  );
}

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

More blocks

All components
Chat with us