Web InnoventixFreeCode

Gradient Band CTA

Original · free

gradient CTA band

byWeb InnoventixReact + Tailwind
ctaxgradientbandcta
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-gradient-band.json
ctax-gradient-band.tsx
"use client";

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

type SubmitStatus = "idle" | "submitting" | "success" | "error";

const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

const GUARANTEES: readonly string[] = [
  "No credit card required",
  "14-day Pro trial",
  "Cancel in one click",
];

function ArrowIcon({ className }: { className?: string }) {
  return (
    <svg
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth={2.2}
      strokeLinecap="round"
      strokeLinejoin="round"
      aria-hidden="true"
      className={className}
    >
      <path d="M5 12h14" />
      <path d="m13 6 6 6-6 6" />
    </svg>
  );
}

function CheckIcon({ className }: { className?: string }) {
  return (
    <svg
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth={2.4}
      strokeLinecap="round"
      strokeLinejoin="round"
      aria-hidden="true"
      className={className}
    >
      <path d="M20 6 9 17l-5-5" />
    </svg>
  );
}

function SparkIcon({ className }: { className?: string }) {
  return (
    <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" className={className}>
      <path d="M12 2c.4 3.9 2.1 5.6 6 6-3.9.4-5.6 2.1-6 6-.4-3.9-2.1-5.6-6-6 3.9-.4 5.6-2.1 6-6Z" />
      <path d="M19 13c.2 1.9 1 2.7 2.9 2.9-1.9.2-2.7 1-2.9 2.9-.2-1.9-1-2.7-2.9-2.9 1.9-.2 2.7-1 2.9-2.9Z" />
    </svg>
  );
}

function StarRow() {
  return (
    <span className="inline-flex gap-0.5" aria-hidden="true">
      {Array.from({ length: 5 }).map((_, i) => (
        <svg key={i} viewBox="0 0 24 24" fill="currentColor" className="h-4 w-4 text-amber-300">
          <path d="m12 2 2.9 6.3 6.9.7-5.1 4.6 1.4 6.8L12 17.8 5.9 20.4l1.4-6.8L2.2 9l6.9-.7L12 2Z" />
        </svg>
      ))}
    </span>
  );
}

export default function CtaxGradientBand() {
  const prefersReducedMotion = useReducedMotion();
  const fieldId = useId();
  const statusId = useId();

  const [email, setEmail] = useState("");
  const [status, setStatus] = useState<SubmitStatus>("idle");

  const invalid = status === "error";

  const handleSubmit = useCallback(
    (event: FormEvent<HTMLFormElement>) => {
      event.preventDefault();
      if (status === "submitting") return;

      if (!EMAIL_PATTERN.test(email.trim())) {
        setStatus("error");
        return;
      }

      setStatus("submitting");
      window.setTimeout(() => {
        setStatus("success");
      }, 1100);
    },
    [email, status],
  );

  const reveal = (delay: number) =>
    prefersReducedMotion
      ? { initial: false as const }
      : {
          initial: { opacity: 0, y: 22 },
          whileInView: { opacity: 1, y: 0 },
          viewport: { once: true, amount: 0.4 },
          transition: { duration: 0.6, delay, ease: [0.16, 1, 0.3, 1] as const },
        };

  return (
    <section className="relative w-full overflow-hidden bg-slate-50 px-4 py-24 sm:py-32 dark:bg-neutral-950">
      <style>{`
        @keyframes ctaxgb-drift-a {
          0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
          50% { transform: translate3d(6%, -8%, 0) scale(1.12); }
        }
        @keyframes ctaxgb-drift-b {
          0%, 100% { transform: translate3d(0, 0, 0) scale(1.05); }
          50% { transform: translate3d(-7%, 6%, 0) scale(0.9); }
        }
        @keyframes ctaxgb-sheen {
          0% { transform: translateX(-140%) skewX(-18deg); }
          60%, 100% { transform: translateX(240%) skewX(-18deg); }
        }
        @keyframes ctaxgb-pop {
          0% { transform: scale(0.5); opacity: 0; }
          60% { transform: scale(1.15); opacity: 1; }
          100% { transform: scale(1); opacity: 1; }
        }
        .ctaxgb-drift-a { animation: ctaxgb-drift-a 16s ease-in-out infinite; }
        .ctaxgb-drift-b { animation: ctaxgb-drift-b 19s ease-in-out infinite; }
        .ctaxgb-sheen { animation: ctaxgb-sheen 4.5s ease-in-out infinite; }
        .ctaxgb-pop { animation: ctaxgb-pop 0.5s cubic-bezier(0.16, 1, 0.3, 1) both; }
        @media (prefers-reduced-motion: reduce) {
          .ctaxgb-drift-a, .ctaxgb-drift-b, .ctaxgb-sheen, .ctaxgb-pop {
            animation: none !important;
          }
        }
      `}</style>

      <div className="mx-auto max-w-6xl">
        <motion.div
          {...reveal(0)}
          className="relative isolate overflow-hidden rounded-[2rem] bg-gradient-to-br from-indigo-600 via-violet-600 to-sky-500 shadow-2xl shadow-indigo-500/25 ring-1 ring-white/15 dark:from-indigo-800 dark:via-violet-800 dark:to-slate-900 dark:shadow-violet-900/40"
        >
          {/* Aurora blobs */}
          <div
            aria-hidden="true"
            className="ctaxgb-drift-a pointer-events-none absolute -left-24 -top-28 h-80 w-80 rounded-full bg-sky-300/40 blur-3xl dark:bg-sky-500/25"
          />
          <div
            aria-hidden="true"
            className="ctaxgb-drift-b pointer-events-none absolute -bottom-32 -right-16 h-96 w-96 rounded-full bg-rose-400/30 blur-3xl dark:bg-violet-500/25"
          />

          {/* Grid texture */}
          <div
            aria-hidden="true"
            className="pointer-events-none absolute inset-0 opacity-[0.18] [mask-image:radial-gradient(ellipse_at_center,black,transparent_75%)]"
            style={{
              backgroundImage:
                "linear-gradient(to right, rgba(255,255,255,0.6) 1px, transparent 1px), linear-gradient(to bottom, rgba(255,255,255,0.6) 1px, transparent 1px)",
              backgroundSize: "44px 44px",
            }}
          />

          <div className="relative grid gap-12 px-6 py-14 sm:px-12 sm:py-16 lg:grid-cols-[1.15fr_1fr] lg:items-center lg:px-16 lg:py-20">
            {/* Copy column */}
            <div className="text-center lg:text-left">
              <motion.span
                {...reveal(0.05)}
                className="inline-flex items-center gap-2 rounded-full bg-white/15 px-4 py-1.5 text-sm font-medium text-white ring-1 ring-inset ring-white/25 backdrop-blur-sm"
              >
                <SparkIcon className="h-4 w-4 text-amber-200" />
                Now with real-time forecasting
              </motion.span>

              <motion.h2
                {...reveal(0.1)}
                className="mt-6 text-balance text-4xl font-semibold leading-[1.05] tracking-tight text-white sm:text-5xl lg:text-6xl"
              >
                Turn scattered data into decisions your team trusts.
              </motion.h2>

              <motion.p
                {...reveal(0.16)}
                className="mx-auto mt-5 max-w-xl text-pretty text-lg leading-relaxed text-indigo-50/90 lg:mx-0"
              >
                Beacon unifies product, revenue, and support signals into one live
                workspace, so you ship the right thing next instead of guessing. Set up
                takes about four minutes.
              </motion.p>

              <motion.div
                {...reveal(0.24)}
                className="mt-8 flex flex-col items-center gap-4 sm:flex-row sm:flex-wrap lg:items-start lg:justify-start"
              >
                <div className="flex items-center gap-3 text-white">
                  <StarRow />
                  <span className="text-sm font-medium text-indigo-50/90">
                    4.9 / 5 from 4,200+ product teams
                  </span>
                </div>
              </motion.div>

              <motion.ul
                {...reveal(0.3)}
                className="mt-6 flex flex-wrap items-center justify-center gap-x-5 gap-y-2 lg:justify-start"
              >
                {GUARANTEES.map((item) => (
                  <li key={item} className="flex items-center gap-2 text-sm font-medium text-indigo-50/90">
                    <span className="grid h-5 w-5 place-items-center rounded-full bg-emerald-400/90 text-emerald-950">
                      <CheckIcon className="h-3 w-3" />
                    </span>
                    {item}
                  </li>
                ))}
              </motion.ul>
            </div>

            {/* Action card column */}
            <motion.div
              {...reveal(0.18)}
              className="rounded-2xl bg-white/95 p-6 shadow-xl ring-1 ring-white/50 backdrop-blur sm:p-8 dark:bg-neutral-900/90 dark:ring-white/10"
            >
              {status === "success" ? (
                <div className="flex flex-col items-center py-6 text-center" aria-live="polite">
                  <span className="ctaxgb-pop grid h-16 w-16 place-items-center rounded-full bg-emerald-500 text-white shadow-lg shadow-emerald-500/40">
                    <CheckIcon className="h-8 w-8" />
                  </span>
                  <h3 className="mt-5 text-xl font-semibold text-slate-900 dark:text-white">
                    You are on the list
                  </h3>
                  <p className="mt-2 max-w-xs text-sm leading-relaxed text-slate-600 dark:text-slate-300">
                    We sent a workspace invite to{" "}
                    <span className="font-medium text-slate-900 dark:text-white">{email.trim()}</span>.
                    Check your inbox to finish setup.
                  </p>
                  <button
                    type="button"
                    onClick={() => {
                      setEmail("");
                      setStatus("idle");
                    }}
                    className="mt-6 rounded-lg px-4 py-2 text-sm font-semibold text-indigo-600 underline-offset-4 transition hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:text-indigo-300"
                  >
                    Use a different email
                  </button>
                </div>
              ) : (
                <form onSubmit={handleSubmit} noValidate className="flex flex-col gap-4">
                  <div className="text-left">
                    <h3 className="text-lg font-semibold text-slate-900 dark:text-white">
                      Start your free trial
                    </h3>
                    <p className="mt-1 text-sm text-slate-500 dark:text-slate-400">
                      Full Pro access for 14 days. No card, no lock-in.
                    </p>
                  </div>

                  <div className="text-left">
                    <label htmlFor={fieldId} className="sr-only">
                      Work email address
                    </label>
                    <input
                      id={fieldId}
                      type="email"
                      name="email"
                      inputMode="email"
                      autoComplete="email"
                      placeholder="you@company.com"
                      value={email}
                      aria-invalid={invalid}
                      aria-describedby={invalid ? statusId : undefined}
                      onChange={(e) => {
                        setEmail(e.target.value);
                        if (status === "error") setStatus("idle");
                      }}
                      className={`w-full rounded-xl border bg-white px-4 py-3 text-base text-slate-900 shadow-sm outline-none transition placeholder:text-slate-400 focus:ring-4 dark:bg-neutral-800 dark:text-white dark:placeholder:text-slate-500 ${
                        invalid
                          ? "border-rose-400 focus:border-rose-500 focus:ring-rose-500/20 dark:border-rose-500/70"
                          : "border-slate-200 focus:border-indigo-500 focus:ring-indigo-500/20 dark:border-neutral-700"
                      }`}
                    />
                    <p
                      id={statusId}
                      aria-live="polite"
                      className={`mt-2 min-h-[1.25rem] text-sm ${
                        invalid ? "text-rose-600 dark:text-rose-400" : "text-transparent"
                      }`}
                    >
                      {invalid ? "Please enter a valid work email address." : "placeholder"}
                    </p>
                  </div>

                  <button
                    type="submit"
                    disabled={status === "submitting"}
                    className="group relative inline-flex items-center justify-center gap-2 overflow-hidden rounded-xl bg-gradient-to-r from-indigo-600 to-violet-600 px-6 py-3.5 text-base font-semibold text-white shadow-lg shadow-indigo-600/30 transition hover:from-indigo-500 hover:to-violet-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:cursor-not-allowed disabled:opacity-80 dark:focus-visible:ring-offset-neutral-900"
                  >
                    {!prefersReducedMotion && status !== "submitting" && (
                      <span
                        aria-hidden="true"
                        className="ctaxgb-sheen absolute inset-y-0 -left-1/2 w-1/2 bg-white/25 blur-md"
                      />
                    )}
                    <span className="relative">
                      {status === "submitting" ? "Creating your workspace..." : "Get started free"}
                    </span>
                    {status !== "submitting" && (
                      <ArrowIcon className="relative h-5 w-5 transition-transform group-hover:translate-x-0.5" />
                    )}
                  </button>

                  <p className="text-center text-xs leading-relaxed text-slate-500 dark:text-slate-400">
                    Prefer a walkthrough?{" "}
                    <a
                      href="#book-demo"
                      className="font-semibold text-indigo-600 underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:text-indigo-300"
                    >
                      Book a 20-minute demo
                    </a>
                  </p>
                </form>
              )}
            </motion.div>
          </div>
        </motion.div>
      </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 →