Social Proof CTA
Original · freeCTA with avatars and rating
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-social-proof.json"use client";
import { useId, useState, type ChangeEvent, type FormEvent } from "react";
import { motion, useReducedMotion, type Variants } from "motion/react";
type Avatar = { initials: string; grad: string };
const AVATARS: Avatar[] = [
{ initials: "AR", grad: "from-indigo-400 to-violet-500" },
{ initials: "MK", grad: "from-emerald-400 to-sky-500" },
{ initials: "JD", grad: "from-rose-400 to-amber-500" },
{ initials: "SL", grad: "from-sky-400 to-indigo-500" },
{ initials: "TN", grad: "from-violet-400 to-rose-500" },
];
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
function Star({ fill }: { fill: number }) {
const clamped = Math.max(0, Math.min(1, fill));
return (
<span className="relative inline-block h-4 w-4">
<svg viewBox="0 0 20 20" aria-hidden="true" className="absolute inset-0 h-4 w-4 text-slate-300 dark:text-slate-600">
<path
fill="currentColor"
d="M10 1.6l2.47 5 5.53.8-4 3.9.94 5.5L10 14.2l-4.94 2.6.94-5.5-4-3.9 5.53-.8z"
/>
</svg>
<span className="absolute inset-0 overflow-hidden" style={{ width: `${clamped * 100}%` }}>
<svg viewBox="0 0 20 20" aria-hidden="true" className="h-4 w-4 text-amber-400">
<path
fill="currentColor"
d="M10 1.6l2.47 5 5.53.8-4 3.9.94 5.5L10 14.2l-4.94 2.6.94-5.5-4-3.9 5.53-.8z"
/>
</svg>
</span>
</span>
);
}
export default function CtaxSocialProof() {
const reduce = useReducedMotion();
const inputId = useId();
const msgId = useId();
const [email, setEmail] = useState("");
const [status, setStatus] = useState<"idle" | "error" | "success">("idle");
const rating = 4.9;
const stars = Array.from({ length: 5 }, (_, i) => Math.max(0, Math.min(1, rating - i)));
function handleSubmit(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
if (!EMAIL_RE.test(email.trim())) {
setStatus("error");
return;
}
setStatus("success");
}
function handleChange(e: ChangeEvent<HTMLInputElement>) {
setEmail(e.target.value);
if (status === "error") setStatus("idle");
}
const containerV: Variants = {
hidden: {},
show: { transition: { staggerChildren: reduce ? 0 : 0.09, delayChildren: reduce ? 0 : 0.05 } },
};
const itemV: Variants = reduce
? { hidden: { opacity: 1, y: 0 }, show: { opacity: 1, y: 0 } }
: {
hidden: { opacity: 0, y: 18 },
show: { opacity: 1, y: 0, transition: { duration: 0.55, ease: "easeOut" } },
};
return (
<section className="relative w-full overflow-hidden bg-slate-50 px-4 py-24 text-slate-900 sm:py-32 dark:bg-slate-950 dark:text-slate-100">
<style>{`
@keyframes ctaxsp-float {
0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
50% { transform: translate3d(0, -22px, 0) scale(1.06); }
}
@keyframes ctaxsp-drift {
0%, 100% { transform: translate3d(0, 0, 0); }
50% { transform: translate3d(18px, 14px, 0); }
}
.ctaxsp-blob-a { animation: ctaxsp-float 13s ease-in-out infinite; }
.ctaxsp-blob-b { animation: ctaxsp-drift 17s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
.ctaxsp-blob-a, .ctaxsp-blob-b { animation: none !important; }
}
`}</style>
{/* Decorative atmosphere */}
<div aria-hidden="true" className="pointer-events-none absolute inset-0 overflow-hidden">
<div className="ctaxsp-blob-a absolute -left-24 top-4 h-72 w-72 rounded-full bg-indigo-400/25 blur-3xl dark:bg-indigo-500/20" />
<div className="ctaxsp-blob-b absolute -right-16 bottom-0 h-80 w-80 rounded-full bg-violet-400/25 blur-3xl dark:bg-violet-600/20" />
<div className="absolute inset-0 bg-[radial-gradient(60%_60%_at_50%_0%,rgba(99,102,241,0.10),transparent)]" />
</div>
<motion.div
variants={containerV}
initial="hidden"
whileInView="show"
viewport={{ once: true, amount: 0.3 }}
className="relative mx-auto max-w-3xl"
>
<div className="rounded-[2rem] border border-slate-200/80 bg-white/70 p-8 shadow-[0_24px_70px_-30px_rgba(30,41,59,0.35)] backdrop-blur-xl sm:p-12 dark:border-slate-800 dark:bg-slate-900/60">
{/* Eyebrow */}
<motion.div variants={itemV} className="flex justify-center">
<span className="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-indigo-50 px-3.5 py-1.5 text-xs font-semibold tracking-wide text-indigo-700 dark:border-indigo-500/30 dark:bg-indigo-500/10 dark:text-indigo-300">
<span className="relative flex h-2 w-2">
<span className="absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-60 motion-safe:animate-ping" />
<span className="relative inline-flex h-2 w-2 rounded-full bg-emerald-500" />
</span>
Trusted by 12,000+ teams
</span>
</motion.div>
{/* Headline */}
<motion.h2
variants={itemV}
className="mt-6 text-center text-3xl font-bold tracking-tight text-balance sm:text-5xl"
>
Ship work you're proud of,{" "}
<span className="bg-gradient-to-r from-indigo-500 via-violet-500 to-sky-500 bg-clip-text text-transparent">
together
</span>
</motion.h2>
{/* Subhead */}
<motion.p
variants={itemV}
className="mx-auto mt-4 max-w-xl text-center text-base leading-relaxed text-slate-600 text-pretty sm:text-lg dark:text-slate-400"
>
Cascade pulls your docs, tasks, and timelines into one calm workspace — so your team
spends less time chasing status updates and more time doing the work that matters.
</motion.p>
{/* Avatars + rating */}
<motion.div
variants={itemV}
className="mt-8 flex flex-col items-center justify-center gap-4 sm:flex-row sm:gap-5"
>
<div className="flex -space-x-3">
{AVATARS.map((a) => (
<span
key={a.initials}
className={`inline-flex h-10 w-10 items-center justify-center rounded-full bg-gradient-to-br ${a.grad} text-xs font-bold text-white ring-2 ring-white dark:ring-slate-900`}
>
{a.initials}
</span>
))}
<span className="inline-flex h-10 w-10 items-center justify-center rounded-full bg-slate-200 text-[10px] font-bold text-slate-700 ring-2 ring-white dark:bg-slate-700 dark:text-slate-100 dark:ring-slate-900">
+12K
</span>
</div>
<div className="hidden h-8 w-px bg-slate-200 sm:block dark:bg-slate-700" />
<div className="flex flex-col items-center sm:items-start">
<div className="flex items-center gap-1.5">
<span className="flex" role="img" aria-label={`Rated ${rating} out of 5`}>
{stars.map((f, i) => (
<Star key={i} fill={f} />
))}
</span>
<span className="text-sm font-bold">{rating}</span>
</div>
<span className="mt-0.5 text-xs text-slate-500 dark:text-slate-400">
from 2,147 verified reviews
</span>
</div>
</motion.div>
{/* CTA form / success */}
<motion.div variants={itemV} className="mt-9">
{status === "success" ? (
<div
id={msgId}
role="status"
aria-live="polite"
className="mx-auto flex max-w-md items-start gap-3 rounded-2xl border border-emerald-200 bg-emerald-50 p-4 text-left dark:border-emerald-500/30 dark:bg-emerald-500/10"
>
<svg viewBox="0 0 20 20" aria-hidden="true" className="mt-0.5 h-5 w-5 shrink-0 text-emerald-500">
<path
fill="currentColor"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.7-9.3a1 1 0 00-1.4-1.4L9 10.6 7.7 9.3a1 1 0 10-1.4 1.4l2 2a1 1 0 001.4 0z"
/>
</svg>
<div>
<p className="text-sm font-semibold text-emerald-800 dark:text-emerald-200">
You're on the list.
</p>
<p className="mt-0.5 text-sm text-emerald-700/90 dark:text-emerald-300/80">
We sent a workspace invite to <span className="font-medium">{email.trim()}</span>.
Check your inbox to finish setting up.
</p>
<button
type="button"
onClick={() => {
setEmail("");
setStatus("idle");
}}
className="mt-2 rounded-md text-sm font-semibold text-emerald-700 underline decoration-emerald-400 underline-offset-2 hover:text-emerald-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500 dark:text-emerald-300 dark:hover:text-emerald-100"
>
Use a different email
</button>
</div>
</div>
) : (
<form onSubmit={handleSubmit} noValidate className="mx-auto max-w-md">
<label htmlFor={inputId} className="sr-only">
Work email address
</label>
<div className="flex flex-col gap-3 sm:flex-row">
<input
id={inputId}
type="email"
inputMode="email"
autoComplete="email"
value={email}
onChange={handleChange}
placeholder="you@company.com"
aria-invalid={status === "error"}
aria-describedby={status === "error" ? msgId : undefined}
className="w-full flex-1 rounded-xl border border-slate-300 bg-white px-4 py-3 text-sm text-slate-900 placeholder:text-slate-400 focus-visible:border-indigo-500 focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-indigo-500/20 dark:border-slate-700 dark:bg-slate-950/60 dark:text-slate-100 dark:placeholder:text-slate-500"
/>
<button
type="submit"
className="inline-flex items-center justify-center gap-2 rounded-xl bg-gradient-to-br from-indigo-500 to-violet-600 px-6 py-3 text-sm font-semibold text-white shadow-lg shadow-indigo-500/25 transition-transform duration-150 hover:-translate-y-0.5 hover:shadow-xl hover:shadow-indigo-500/30 focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-indigo-500/40 active:translate-y-0"
>
Start free trial
<svg viewBox="0 0 20 20" aria-hidden="true" className="h-4 w-4">
<path
fill="currentColor"
d="M10.5 3.3a1 1 0 00-1.4 1.4L12.6 8H4a1 1 0 100 2h8.6l-3.5 3.3a1 1 0 101.4 1.4l5-5a1 1 0 000-1.4z"
/>
</svg>
</button>
</div>
<p
id={msgId}
role={status === "error" ? "alert" : undefined}
aria-live="polite"
className={`mt-2 min-h-[1.25rem] text-center text-xs sm:text-left ${
status === "error"
? "text-rose-600 dark:text-rose-400"
: "text-slate-500 dark:text-slate-400"
}`}
>
{status === "error"
? "Please enter a valid email address."
: "14-day free trial · No credit card required · Cancel anytime."}
</p>
</form>
)}
</motion.div>
{/* Micro social-proof footer */}
<motion.div
variants={itemV}
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"
>
<span className="inline-flex items-center gap-1.5">
<svg viewBox="0 0 20 20" aria-hidden="true" className="h-4 w-4 text-emerald-500">
<path
fill="currentColor"
d="M10 1l7 3v5c0 4.4-3 8.3-7 10-4-1.7-7-5.6-7-10V4z"
opacity="0.25"
/>
<path
fill="currentColor"
d="M8.9 12.2L6.7 10a1 1 0 011.4-1.4l1.2 1.2 3-3.3a1 1 0 011.5 1.3l-3.7 4a1 1 0 01-1.4.1z"
/>
</svg>
SOC 2 Type II certified
</span>
<span className="inline-flex items-center gap-1.5">
<svg viewBox="0 0 20 20" aria-hidden="true" className="h-4 w-4 text-indigo-500">
<path
fill="currentColor"
d="M10 2a4 4 0 014 4v2h1a1 1 0 011 1v7a1 1 0 01-1 1H5a1 1 0 01-1-1V9a1 1 0 011-1h1V6a4 4 0 014-4zm2 6V6a2 2 0 10-4 0v2z"
/>
</svg>
99.98% uptime SLA
</span>
<span className="inline-flex items-center gap-1.5">
<svg viewBox="0 0 20 20" aria-hidden="true" className="h-4 w-4 text-amber-500">
<path
fill="currentColor"
d="M10 1.6l2.47 5 5.53.8-4 3.9.94 5.5L10 14.2l-4.94 2.6.94-5.5-4-3.9 5.53-.8z"
/>
</svg>
Rated #1 in team workflow
</span>
</motion.div>
</div>
</motion.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

