Stats CTA CTA
Original · freeCTA with supporting stats
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-stats-cta.json"use client";
import { useEffect, useRef, useState } from "react";
import type { FormEvent } from "react";
import { motion, useInView, useReducedMotion } from "motion/react";
type Stat = {
label: string;
value: number;
prefix?: string;
suffix?: string;
decimals?: number;
};
const STATS: Stat[] = [
{ label: "Teams shipping with Nimbus", value: 12000, suffix: "+" },
{ label: "Median platform uptime", value: 99.98, suffix: "%", decimals: 2 },
{ label: "Faster release cycles", value: 3.4, suffix: "x", decimals: 1 },
{ label: "Native integrations", value: 150, suffix: "+" },
];
type Status = "idle" | "invalid" | "success";
function formatNumber(value: number, decimals: number): string {
return new Intl.NumberFormat("en-US", {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals,
}).format(value);
}
function CountUp({ stat, play }: { stat: Stat; play: boolean }) {
const reduce = useReducedMotion();
const [display, setDisplay] = useState(0);
useEffect(() => {
if (!play) return;
if (reduce) {
setDisplay(stat.value);
return;
}
let raf = 0;
const duration = 1500;
const start = performance.now();
const tick = (now: number) => {
const t = Math.min(1, (now - start) / duration);
const eased = 1 - Math.pow(1 - t, 3);
setDisplay(stat.value * eased);
if (t < 1) raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
return () => cancelAnimationFrame(raf);
}, [play, reduce, stat.value]);
const decimals = stat.decimals ?? 0;
return (
<span className="tabular-nums">
{stat.prefix}
{formatNumber(display, decimals)}
{stat.suffix}
</span>
);
}
export default function CtaxStatsCta() {
const reduce = useReducedMotion();
const statsRef = useRef<HTMLDivElement | null>(null);
const inView = useInView(statsRef, { once: true, amount: 0.4 });
const [email, setEmail] = useState("");
const [status, setStatus] = useState<Status>("idle");
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim());
setStatus(valid ? "success" : "invalid");
};
const initial = reduce ? undefined : { opacity: 0, y: 24 };
const animate = reduce ? undefined : { opacity: 1, y: 0 };
return (
<section className="relative w-full overflow-hidden bg-slate-50 px-6 py-24 sm:py-28 md:py-32 dark:bg-slate-950">
<style>{`
@keyframes ctaxsc-float {
0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
50% { transform: translate3d(0, -28px, 0) scale(1.06); }
}
@keyframes ctaxsc-drift {
0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
50% { transform: translate3d(24px, 18px, 0) scale(0.94); }
}
@keyframes ctaxsc-pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.55; transform: scale(0.7); }
}
@keyframes ctaxsc-sheen {
0% { transform: translateX(-140%) skewX(-18deg); }
60%, 100% { transform: translateX(260%) skewX(-18deg); }
}
.ctaxsc-blob-a { animation: ctaxsc-float 14s ease-in-out infinite; }
.ctaxsc-blob-b { animation: ctaxsc-drift 18s ease-in-out infinite; }
.ctaxsc-dot { animation: ctaxsc-pulse 2.4s ease-in-out infinite; }
.ctaxsc-sheen { animation: ctaxsc-sheen 3.6s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
.ctaxsc-blob-a,
.ctaxsc-blob-b,
.ctaxsc-dot,
.ctaxsc-sheen {
animation: none !important;
}
}
`}</style>
{/* Decorative background */}
<div aria-hidden className="pointer-events-none absolute inset-0 overflow-hidden">
<div className="ctaxsc-blob-a absolute -left-24 -top-24 h-80 w-80 rounded-full bg-indigo-300/40 blur-3xl dark:bg-indigo-600/25" />
<div className="ctaxsc-blob-b absolute -bottom-32 -right-16 h-96 w-96 rounded-full bg-violet-300/40 blur-3xl dark:bg-violet-600/25" />
<div
className="absolute inset-0 opacity-[0.4] dark:opacity-[0.25]"
style={{
backgroundImage:
"linear-gradient(to right, rgba(100,116,139,0.18) 1px, transparent 1px), linear-gradient(to bottom, rgba(100,116,139,0.18) 1px, transparent 1px)",
backgroundSize: "48px 48px",
maskImage:
"radial-gradient(ellipse 70% 60% at 50% 40%, black 40%, transparent 100%)",
WebkitMaskImage:
"radial-gradient(ellipse 70% 60% at 50% 40%, black 40%, transparent 100%)",
}}
/>
</div>
<div className="relative mx-auto max-w-5xl">
<motion.div
initial={initial}
whileInView={animate}
viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.6, ease: "easeOut" }}
className="flex flex-col items-center text-center"
>
{/* Eyebrow badge */}
<span className="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-white/70 px-4 py-1.5 text-xs font-semibold uppercase tracking-wider text-indigo-700 backdrop-blur dark:border-indigo-500/30 dark:bg-white/5 dark:text-indigo-300">
<span className="ctaxsc-dot h-1.5 w-1.5 rounded-full bg-emerald-500 dark:bg-emerald-400" />
Ship faster, break less
</span>
{/* Headline */}
<h2 className="mt-6 max-w-3xl text-balance text-4xl font-bold tracking-tight text-slate-900 sm:text-5xl md:text-6xl dark:text-white">
Turn scattered work into one calm, shippable pipeline
</h2>
{/* Subtext */}
<p className="mt-6 max-w-2xl text-pretty text-base leading-relaxed text-slate-600 sm:text-lg dark:text-slate-300">
Nimbus unifies planning, code review, and release tracking so your
team spends less time in stand-ups and more time shipping. Start
free, no card and no lock-in.
</p>
{/* Email form */}
<form
onSubmit={handleSubmit}
noValidate
className="mt-9 flex w-full max-w-md flex-col gap-3 sm:flex-row"
>
<label htmlFor="ctaxsc-email" className="sr-only">
Work email address
</label>
<input
id="ctaxsc-email"
type="email"
inputMode="email"
autoComplete="email"
value={email}
onChange={(e) => {
setEmail(e.target.value);
if (status !== "idle") setStatus("idle");
}}
placeholder="you@company.com"
aria-invalid={status === "invalid"}
aria-describedby="ctaxsc-status"
className="w-full 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 dark:border-slate-700 dark:bg-slate-900 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-indigo-600 px-6 py-3 text-sm font-semibold text-white shadow-lg shadow-indigo-600/25 outline-none transition hover:bg-indigo-500 focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-50 active:scale-[0.98] dark:focus-visible:ring-offset-slate-950"
>
<span
aria-hidden
className="ctaxsc-sheen pointer-events-none absolute inset-y-0 left-0 w-1/3 bg-white/25 blur-md"
/>
<span className="relative">Start free trial</span>
<svg
aria-hidden
viewBox="0 0 20 20"
fill="none"
className="relative h-4 w-4 transition-transform group-hover:translate-x-0.5"
>
<path
d="M4 10h11M11 5.5 15.5 10 11 14.5"
stroke="currentColor"
strokeWidth="1.75"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
</form>
{/* Status / microcopy */}
<p
id="ctaxsc-status"
aria-live="polite"
className="mt-3 min-h-5 text-sm"
>
{status === "success" ? (
<span className="inline-flex items-center gap-1.5 font-medium text-emerald-600 dark:text-emerald-400">
<svg aria-hidden viewBox="0 0 20 20" className="h-4 w-4" fill="none">
<path
d="m5 10.5 3 3 7-7.5"
stroke="currentColor"
strokeWidth="1.75"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
You are in. Check your inbox to activate the trial.
</span>
) : status === "invalid" ? (
<span className="font-medium text-rose-600 dark:text-rose-400">
Please enter a valid work email address.
</span>
) : (
<span className="text-slate-500 dark:text-slate-400">
14-day trial · Cancel anytime · SOC 2 Type II
</span>
)}
</p>
{/* Secondary CTA */}
<div className="mt-6">
<a
href="#book-demo"
className="inline-flex items-center gap-1.5 rounded-lg px-2 py-1 text-sm font-semibold text-slate-700 underline-offset-4 outline-none transition hover:text-indigo-600 hover:underline focus-visible:ring-2 focus-visible:ring-indigo-500 dark:text-slate-200 dark:hover:text-indigo-300"
>
Book a 20-minute demo
<svg aria-hidden viewBox="0 0 20 20" fill="none" className="h-3.5 w-3.5">
<path
d="M7 5.5 11.5 10 7 14.5"
stroke="currentColor"
strokeWidth="1.75"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</a>
</div>
</motion.div>
{/* Supporting stats */}
<div
ref={statsRef}
className="mt-16 grid grid-cols-2 gap-px overflow-hidden rounded-2xl border border-slate-200 bg-slate-200 shadow-sm md:grid-cols-4 dark:border-slate-800 dark:bg-slate-800"
>
{STATS.map((stat, i) => (
<motion.div
key={stat.label}
initial={initial}
animate={inView ? animate : initial}
transition={{ duration: 0.5, ease: "easeOut", delay: 0.08 * i }}
className="flex flex-col items-center gap-1.5 bg-white px-4 py-8 text-center dark:bg-slate-900"
>
<span className="text-3xl font-bold tracking-tight text-indigo-600 sm:text-4xl dark:text-indigo-400">
<CountUp stat={stat} play={inView} />
</span>
<span className="text-xs font-medium leading-snug text-slate-500 sm:text-sm dark:text-slate-400">
{stat.label}
</span>
</motion.div>
))}
</div>
<p className="mt-6 text-center text-xs uppercase tracking-wider text-slate-400 dark:text-slate-500">
Trusted by product teams in 40+ countries
</p>
</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

Gradient Band CTA
Originalgradient CTA band

Newsletter CTA
OriginalCTA with newsletter input

App Download CTA
Originalapp download CTA with store buttons

