AIEO-ready websites that get cited by AITalk to us

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.

React + Tailwindnpm: motionanimated border ctaconic gradient borderemail capture ctaframer motion newsletterglowing border card
Original · free to use
npx shadcn@latest add https://webinnoventix.com/r/acta-border-beam-cta.json
acta-border-beam-cta.tsx
"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 quote

More blocks

All components
Chat with us