Gradient Band CTA
Original · freegradient CTA band
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"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
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 quoteSimilar components
Browse all →
Gradient CTA
OriginalA bold gradient call-to-action banner with two buttons.

Full-Bleed Dark CTA
OriginalA full-bleed dark call-to-action banner with a live status badge, subtle grid backdrop and a primary plus secondary button pair to drive discovery calls.

Split CTA With Lead Form
OriginalA two-column call-to-action that pairs a benefit checklist with an inline name, email and message form for capturing qualified leads directly in the section.

Newsletter CTA With Stats
OriginalA newsletter sign-up call-to-action combining an inline email subscribe form with a three-cell social-proof stat strip to boost conversions.

Aurora Glow CTA Banner
OriginalA full-bleed dark call-to-action banner with a rotating aurora gradient, floating orbs and a shine-on-hover button, with staggered blur-in text that reveals on scroll.

Animated Border Beam CTA
OriginalAn email-capture call-to-action card wrapped in a rotating conic-gradient border beam, with staggered reveal, animated shine text and an inline success state on submit.

Marquee Ribbon CTA
OriginalA dark call-to-action band framed by two infinite scrolling tag ribbons with a continuously flowing animated-gradient button and staggered centre content.

Centered CTA
Originalcentred CTA band

Split CTA
Originalsplit CTA with image side

Newsletter CTA
OriginalCTA with newsletter input

App Download CTA
Originalapp download CTA with store buttons

Stats CTA CTA
OriginalCTA with supporting stats

