Web InnoventixFreeCode

Centered CTA

Original · free

centred CTA band

byWeb InnoventixReact + Tailwind
ctaxcenteredcta
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/ctax-centered.json
ctax-centered.tsx
"use client";

import { useId, useState } from "react";
import type { FormEvent } from "react";
import { motion, useReducedMotion } from "motion/react";

type Status = "idle" | "error" | "success";

export default function CtaxCentered() {
  const reduce = useReducedMotion();
  const emailId = useId();
  const errId = useId();
  const [email, setEmail] = useState("");
  const [status, setStatus] = useState<Status>("idle");

  function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim());
    if (!valid) {
      setStatus("error");
      return;
    }
    setStatus("success");
  }

  const reveal = (delay: number) =>
    reduce
      ? { initial: false as const }
      : {
          initial: { opacity: 0, y: 22 },
          whileInView: { opacity: 1, y: 0 },
          viewport: { once: true, margin: "-70px" },
          transition: {
            duration: 0.65,
            delay,
            ease: [0.16, 1, 0.3, 1] as [number, number, number, number],
          },
        };

  return (
    <section className="relative w-full overflow-hidden bg-gradient-to-b from-white to-slate-100 px-6 py-24 sm:py-32 dark:from-slate-950 dark:to-slate-900">
      <style>{`
        @keyframes ctaxc-drift {
          0%   { transform: translate3d(0, 0, 0) scale(1); }
          33%  { transform: translate3d(4%, -6%, 0) scale(1.08); }
          66%  { transform: translate3d(-5%, 4%, 0) scale(0.95); }
          100% { transform: translate3d(0, 0, 0) scale(1); }
        }
        @keyframes ctaxc-drift-slow {
          0%   { transform: translate3d(0, 0, 0) scale(1); }
          50%  { transform: translate3d(-6%, 5%, 0) scale(1.12); }
          100% { transform: translate3d(0, 0, 0) scale(1); }
        }
        @keyframes ctaxc-ping {
          0%   { transform: scale(1); opacity: 0.7; }
          70%  { transform: scale(2.6); opacity: 0; }
          100% { transform: scale(2.6); opacity: 0; }
        }
        @keyframes ctaxc-sheen {
          0%   { transform: translateX(-140%) skewX(-18deg); }
          60%, 100% { transform: translateX(260%) skewX(-18deg); }
        }
        .ctaxc-blob-a { animation: ctaxc-drift 22s ease-in-out infinite; }
        .ctaxc-blob-b { animation: ctaxc-drift-slow 28s ease-in-out infinite; }
        .ctaxc-blob-c { animation: ctaxc-drift 26s ease-in-out infinite reverse; }
        .ctaxc-ping   { animation: ctaxc-ping 2.8s cubic-bezier(0,0,0.2,1) infinite; }
        .ctaxc-sheen  { animation: ctaxc-sheen 4.5s ease-in-out infinite; }
        @media (prefers-reduced-motion: reduce) {
          .ctaxc-blob-a, .ctaxc-blob-b, .ctaxc-blob-c,
          .ctaxc-ping, .ctaxc-sheen { animation: none !important; }
        }
      `}</style>

      {/* Atmospheric background — gradients, blobs, and a faint grid */}
      <div aria-hidden="true" className="pointer-events-none absolute inset-0 overflow-hidden">
        <div className="ctaxc-blob-a absolute -top-24 left-1/2 h-[34rem] w-[34rem] -translate-x-[70%] rounded-full bg-indigo-400/40 blur-3xl dark:bg-indigo-600/30" />
        <div className="ctaxc-blob-b absolute -top-16 left-1/2 h-[30rem] w-[30rem] -translate-x-[10%] rounded-full bg-violet-400/40 blur-3xl dark:bg-violet-600/25" />
        <div className="ctaxc-blob-c absolute -bottom-32 left-1/2 h-[32rem] w-[32rem] -translate-x-1/2 rounded-full bg-sky-300/40 blur-3xl dark:bg-sky-600/20" />
        <svg
          className="absolute inset-0 h-full w-full text-slate-900/[0.05] dark:text-white/[0.06]"
          xmlns="http://www.w3.org/2000/svg"
        >
          <defs>
            <pattern id="ctaxc-grid" width="44" height="44" patternUnits="userSpaceOnUse">
              <path d="M44 0H0V44" fill="none" stroke="currentColor" strokeWidth="1" />
            </pattern>
            <radialGradient id="ctaxc-fade" cx="50%" cy="42%" r="60%">
              <stop offset="0%" stopColor="white" stopOpacity="1" />
              <stop offset="100%" stopColor="white" stopOpacity="0" />
            </radialGradient>
            <mask id="ctaxc-mask">
              <rect width="100%" height="100%" fill="url(#ctaxc-fade)" />
            </mask>
          </defs>
          <rect width="100%" height="100%" fill="url(#ctaxc-grid)" mask="url(#ctaxc-mask)" />
        </svg>
      </div>

      <div className="relative mx-auto flex max-w-3xl flex-col items-center text-center">
        {/* Eyebrow badge */}
        <motion.span
          {...reveal(0)}
          className="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white/70 px-4 py-1.5 text-xs font-medium tracking-wide text-slate-700 shadow-sm backdrop-blur dark:border-white/10 dark:bg-white/5 dark:text-slate-200"
        >
          <span className="relative flex h-2 w-2">
            <span className="ctaxc-ping absolute inline-flex h-full w-full rounded-full bg-emerald-500" />
            <span className="relative inline-flex h-2 w-2 rounded-full bg-emerald-500" />
          </span>
          New in Cadence — automated project handoffs
        </motion.span>

        {/* Headline */}
        <motion.h2
          {...reveal(0.08)}
          className="mt-7 text-4xl font-semibold tracking-tight text-slate-900 sm:text-6xl dark:text-white"
        >
          Ship the work,
          <br className="hidden sm:block" />{" "}
          <span className="bg-gradient-to-r from-indigo-600 via-violet-600 to-sky-500 bg-clip-text text-transparent dark:from-indigo-400 dark:via-violet-400 dark:to-sky-400">
            skip the status meeting
          </span>
        </motion.h2>

        {/* Subhead */}
        <motion.p
          {...reveal(0.16)}
          className="mt-6 max-w-xl text-base leading-relaxed text-slate-600 sm:text-lg dark:text-slate-300"
        >
          Cadence folds scattered updates into one live source of truth, so your
          team spends its hours building the product instead of reporting on it.
        </motion.p>

        {/* Email capture form */}
        <motion.div {...reveal(0.24)} className="mt-10 w-full max-w-md">
          {status === "success" ? (
            <div
              role="status"
              aria-live="polite"
              className="flex items-center justify-center gap-3 rounded-2xl border border-emerald-300/70 bg-emerald-50 px-5 py-4 text-sm font-medium text-emerald-800 dark:border-emerald-500/30 dark:bg-emerald-500/10 dark:text-emerald-200"
            >
              <svg
                aria-hidden="true"
                viewBox="0 0 20 20"
                fill="currentColor"
                className="h-5 w-5 shrink-0 text-emerald-600 dark:text-emerald-400"
              >
                <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 9.7a1 1 0 1 1 1.4-1.4l3.3 3.29 6.8-6.8a1 1 0 0 1 1.4 0Z"
                  clipRule="evenodd"
                />
              </svg>
              You&apos;re in. Check your inbox to set up your workspace.
            </div>
          ) : (
            <form onSubmit={handleSubmit} noValidate className="flex flex-col gap-3">
              <div className="flex flex-col gap-3 sm:flex-row">
                <label htmlFor={emailId} className="sr-only">
                  Work email address
                </label>
                <input
                  id={emailId}
                  type="email"
                  inputMode="email"
                  autoComplete="email"
                  placeholder="you@company.com"
                  value={email}
                  onChange={(event) => {
                    setEmail(event.target.value);
                    if (status === "error") setStatus("idle");
                  }}
                  aria-invalid={status === "error"}
                  aria-describedby={status === "error" ? errId : undefined}
                  className="min-w-0 flex-1 rounded-xl border border-slate-300 bg-white px-4 py-3 text-sm text-slate-900 shadow-sm outline-none transition placeholder:text-slate-400 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/40 aria-[invalid=true]:border-rose-400 aria-[invalid=true]:ring-2 aria-[invalid=true]:ring-rose-400/40 dark:border-white/15 dark:bg-white/5 dark:text-white dark:placeholder:text-slate-500"
                />
                <button
                  type="submit"
                  className="group relative inline-flex items-center justify-center gap-2 overflow-hidden rounded-xl bg-slate-900 px-6 py-3 text-sm font-semibold text-white shadow-lg shadow-indigo-500/20 outline-none transition hover:bg-slate-800 focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:bg-white dark:text-slate-900 dark:hover:bg-slate-100 dark:focus-visible:ring-offset-slate-950"
                >
                  <span className="ctaxc-sheen pointer-events-none absolute inset-y-0 -left-1 w-1/3 bg-white/25 blur-md dark:bg-slate-900/10" />
                  <span className="relative">Start free</span>
                  <svg
                    aria-hidden="true"
                    viewBox="0 0 20 20"
                    fill="none"
                    className="relative h-4 w-4 transition-transform duration-200 group-hover:translate-x-0.5"
                  >
                    <path
                      d="M4 10h11m0 0-4-4m4 4-4 4"
                      stroke="currentColor"
                      strokeWidth="1.8"
                      strokeLinecap="round"
                      strokeLinejoin="round"
                    />
                  </svg>
                </button>
              </div>
              {status === "error" ? (
                <p id={errId} role="alert" className="text-left text-xs font-medium text-rose-600 dark:text-rose-400">
                  Please enter a valid work email so we can send your invite.
                </p>
              ) : (
                <p className="text-left text-xs text-slate-500 dark:text-slate-400">
                  14-day trial · no credit card · cancel anytime.
                </p>
              )}
            </form>
          )}

          <div className="mt-4 flex items-center justify-center">
            <a
              href="#demo"
              className="inline-flex items-center gap-1.5 text-sm font-medium text-slate-700 underline-offset-4 transition hover:text-indigo-600 hover:underline dark:text-slate-300 dark:hover:text-indigo-400"
            >
              Or book a 20-minute demo
              <svg aria-hidden="true" viewBox="0 0 20 20" fill="none" className="h-3.5 w-3.5">
                <path
                  d="M7 5l5 5-5 5"
                  stroke="currentColor"
                  strokeWidth="1.8"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                />
              </svg>
            </a>
          </div>
        </motion.div>

        {/* Social proof */}
        <motion.div
          {...reveal(0.32)}
          className="mt-12 flex flex-col items-center gap-4 sm:flex-row sm:gap-6"
        >
          <div className="flex items-center">
            <div className="flex -space-x-3">
              {[
                "from-indigo-400 to-violet-500",
                "from-sky-400 to-indigo-500",
                "from-emerald-400 to-sky-500",
                "from-amber-400 to-rose-500",
                "from-violet-400 to-fuchsia-500",
              ].map((swatch, index) => (
                <span
                  key={index}
                  className={`h-9 w-9 rounded-full bg-gradient-to-br ${swatch} ring-2 ring-white dark:ring-slate-950`}
                />
              ))}
            </div>
            <span className="ml-4 text-sm font-medium text-slate-700 dark:text-slate-300">
              3,200+ product teams
            </span>
          </div>

          <span className="hidden h-5 w-px bg-slate-300 sm:block dark:bg-white/15" />

          <div className="flex items-center gap-2">
            <div className="flex" aria-hidden="true">
              {Array.from({ length: 5 }).map((_, index) => (
                <svg key={index} viewBox="0 0 20 20" fill="currentColor" className="h-4 w-4 text-amber-400">
                  <path d="M10 1.5l2.6 5.3 5.9.85-4.25 4.14 1 5.86L10 15.9l-5.25 2.75 1-5.86L1.5 7.65l5.9-.85L10 1.5Z" />
                </svg>
              ))}
            </div>
            <span className="text-sm text-slate-600 dark:text-slate-400">
              <span className="font-semibold text-slate-800 dark:text-slate-200">4.9/5</span>{" "}
              across 600+ reviews
            </span>
          </div>
        </motion.div>

        {/* Assurance row */}
        <motion.ul
          {...reveal(0.4)}
          className="mt-8 flex flex-wrap items-center justify-center gap-x-6 gap-y-2 text-xs font-medium text-slate-500 dark:text-slate-400"
        >
          {["SOC 2 Type II", "99.99% uptime SLA", "Migrate in a day"].map((item) => (
            <li key={item} className="inline-flex items-center gap-1.5">
              <svg aria-hidden="true" viewBox="0 0 20 20" fill="currentColor" className="h-3.5 w-3.5 text-emerald-500">
                <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 9.7a1 1 0 1 1 1.4-1.4l3.3 3.29 6.8-6.8a1 1 0 0 1 1.4 0Z"
                  clipRule="evenodd"
                />
              </svg>
              {item}
            </li>
          ))}
        </motion.ul>
      </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 →