Animated Border Beam CTA
An 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.
npx shadcn@latest add https://webinnoventix.com/r/acta-border-beam-cta.json"use client";
import { useState } from "react";
import { AnimatePresence, motion, useReducedMotion, type Variants } from "motion/react";
export default function ActaBorderBeamCta() {
const reduce = useReducedMotion();
const [email, setEmail] = useState("");
const [sent, setSent] = useState(false);
function onSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
if (!email) return;
setSent(true);
}
const container: Variants = {
hidden: {},
show: { transition: { staggerChildren: 0.09, delayChildren: 0.1 } },
};
const rise: Variants = {
hidden: { opacity: 0, y: reduce ? 0 : 20 },
show: { opacity: 1, y: 0, transition: { duration: 0.55, ease: "easeOut" } },
};
return (
<section className="relative isolate overflow-hidden bg-zinc-50 px-6 py-20 dark:bg-zinc-950 md:py-28">
<style>{`
@keyframes acta-beam-spin { to { transform: translate(-50%, -50%) rotate(360deg); } }
@keyframes acta-text-shine { to { background-position: 200% center; } }
.acta-beam { animation: acta-beam-spin 6s linear infinite; }
.acta-shine { background-size: 200% auto; animation: acta-text-shine 5s linear infinite; }
@media (prefers-reduced-motion: reduce) {
.acta-beam, .acta-shine { animation: none; }
}
`}</style>
<div aria-hidden className="pointer-events-none absolute inset-0 -z-10">
<div className="absolute left-1/2 top-0 h-72 w-[42rem] max-w-full -translate-x-1/2 rounded-full bg-gradient-to-r from-indigo-300 via-violet-300 to-sky-300 opacity-40 blur-3xl dark:opacity-20" />
</div>
<motion.div
variants={container}
initial="hidden"
whileInView="show"
viewport={{ once: true, amount: 0.3 }}
className="relative mx-auto max-w-3xl"
>
<div className="relative overflow-hidden rounded-[1.75rem] p-[1.5px]">
<div
aria-hidden
className="acta-beam absolute left-1/2 top-1/2 h-[60rem] w-[60rem] [background:conic-gradient(from_0deg,transparent_0deg,transparent_250deg,#6366f1_300deg,#d946ef_330deg,#38bdf8_355deg,transparent_360deg)]"
/>
<div className="relative rounded-[1.65rem] bg-white px-8 py-14 text-center shadow-xl shadow-zinc-900/5 dark:bg-zinc-900 dark:shadow-black/40 sm:px-14">
<motion.span
variants={rise}
className="inline-flex items-center gap-2 rounded-full border border-zinc-200 bg-zinc-50 px-3.5 py-1 text-xs font-medium text-zinc-600 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300"
>
<svg viewBox="0 0 24 24" className="h-3.5 w-3.5 fill-indigo-500" aria-hidden>
<path d="M12 2l2.5 6.5L21 11l-6.5 2.5L12 20l-2.5-6.5L3 11l6.5-2.5L12 2z" />
</svg>
Limited early access
</motion.span>
<motion.h2
variants={rise}
className="mt-5 text-3xl font-semibold tracking-tight text-zinc-900 dark:text-white sm:text-4xl"
>
Join the list before the{" "}
<span className="acta-shine bg-gradient-to-r from-indigo-600 via-fuchsia-500 to-indigo-600 bg-clip-text text-transparent dark:from-indigo-400 dark:via-fuchsia-400 dark:to-indigo-400">
doors open
</span>
</motion.h2>
<motion.p variants={rise} className="mx-auto mt-4 max-w-md text-base text-zinc-600 dark:text-zinc-400">
Be first to try it, shape the roadmap and lock in founder pricing for life.
</motion.p>
<motion.div variants={rise} className="mx-auto mt-8 max-w-md">
<AnimatePresence mode="wait" initial={false}>
{sent ? (
<motion.div
key="ok"
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -8 }}
className="flex items-center justify-center gap-2 rounded-full border border-emerald-200 bg-emerald-50 px-5 py-3 text-sm font-medium text-emerald-700 dark:border-emerald-500/30 dark:bg-emerald-500/10 dark:text-emerald-300"
>
<svg viewBox="0 0 24 24" className="h-5 w-5" fill="none" stroke="currentColor" strokeWidth={2.5} strokeLinecap="round" strokeLinejoin="round" aria-hidden>
<path d="M20 6L9 17l-5-5" />
</svg>
You are on the list. Check your inbox to confirm.
</motion.div>
) : (
<motion.form
key="form"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onSubmit={onSubmit}
className="flex flex-col gap-3 sm:flex-row"
>
<label htmlFor="acta-beam-email" className="sr-only">
Email address
</label>
<input
id="acta-beam-email"
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="you@company.com"
className="w-full flex-1 rounded-full border border-zinc-200 bg-white px-5 py-3 text-sm text-zinc-900 shadow-sm outline-none transition placeholder:text-zinc-400 focus:border-indigo-400 focus:ring-2 focus:ring-indigo-500/30 dark:border-zinc-700 dark:bg-zinc-800 dark:text-white dark:placeholder:text-zinc-500"
/>
<motion.button
whileHover={reduce ? undefined : { y: -1 }}
whileTap={{ scale: 0.97 }}
type="submit"
className="group relative inline-flex items-center justify-center gap-2 overflow-hidden rounded-full bg-zinc-900 px-6 py-3 text-sm font-semibold text-white transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:bg-white dark:text-zinc-900 dark:focus-visible:ring-offset-zinc-900"
>
<span className="relative z-10">Notify me</span>
<span
aria-hidden
className="absolute inset-0 -translate-x-full bg-gradient-to-r from-transparent via-white/30 to-transparent transition-transform duration-700 ease-out group-hover:translate-x-full dark:via-zinc-900/20"
/>
</motion.button>
</motion.form>
)}
</AnimatePresence>
</motion.div>
<motion.p variants={rise} className="mt-5 text-xs text-zinc-500 dark:text-zinc-500">
No spam, ever. One short email when we launch.
</motion.p>
</div>
</div>
</motion.div>
</section>
);
}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 quoteMore blocks
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.

