Web InnoventixFreeCode

Centered Gradient Hero

Original · free

centred hero on a gradient

byWeb InnoventixReact + Tailwind
heroxcenteredgradientheroes
Open

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/herox-centered-gradient.json
herox-centered-gradient.tsx
"use client";

import { useState, type ChangeEvent, type FormEvent } from "react";
import { motion, useReducedMotion, type Variants } from "motion/react";

type AudienceId = "startups" | "enterprise" | "agencies";
type FormStatus = "idle" | "error" | "success";

interface Audience {
  id: AudienceId;
  label: string;
  blurb: string;
}

const AUDIENCES: Audience[] = [
  {
    id: "startups",
    label: "Startups",
    blurb: "Built for lean teams shipping fast on a short runway.",
  },
  {
    id: "enterprise",
    label: "Enterprise",
    blurb: "SSO, granular roles, and audit logs your security team will actually sign off on.",
  },
  {
    id: "agencies",
    label: "Agencies",
    blurb: "Run every client workspace from one dashboard — no more account switching.",
  },
];

const HXCG_CSS = `
@keyframes hxcg-float-a {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50% { transform: translate3d(0, -34px, 0) scale(1.07); }
}
@keyframes hxcg-float-b {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50% { transform: translate3d(26px, 22px, 0) scale(1.09); }
}
@keyframes hxcg-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.35; transform: scale(0.7); }
}
@keyframes hxcg-spin {
  to { transform: translateX(-50%) rotate(360deg); }
}
.hxcg-grid {
  background-image:
    linear-gradient(to right, rgba(100, 116, 139, 0.14) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(100, 116, 139, 0.14) 1px, transparent 1px);
  background-size: 56px 56px;
  -webkit-mask-image: radial-gradient(ellipse 72% 62% at 50% 0%, #000 38%, transparent 80%);
  mask-image: radial-gradient(ellipse 72% 62% at 50% 0%, #000 38%, transparent 80%);
}
.hxcg-halo {
  background: conic-gradient(
    from 0deg,
    rgba(99, 102, 241, 0),
    rgba(99, 102, 241, 0.38),
    rgba(168, 85, 247, 0.38),
    rgba(56, 189, 248, 0.38),
    rgba(99, 102, 241, 0)
  );
  animation: hxcg-spin 28s linear infinite;
}
.hxcg-orb-a { animation: hxcg-float-a 15s ease-in-out infinite; }
.hxcg-orb-b { animation: hxcg-float-b 19s ease-in-out infinite; }
.hxcg-orb-c { animation: hxcg-float-a 23s ease-in-out infinite reverse; }
.hxcg-dot { animation: hxcg-pulse 2.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .hxcg-halo, .hxcg-orb-a, .hxcg-orb-b, .hxcg-orb-c, .hxcg-dot {
    animation: none !important;
  }
}
`;

const container: Variants = {
  hidden: {},
  show: { transition: { staggerChildren: 0.08, delayChildren: 0.05 } },
};

const item: Variants = {
  hidden: { opacity: 0, y: 18 },
  show: { opacity: 1, y: 0, transition: { duration: 0.5, ease: "easeOut" } },
};

const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

export default function HeroxCenteredGradient() {
  const prefersReducedMotion = useReducedMotion();
  const [audience, setAudience] = useState<AudienceId>("startups");
  const [email, setEmail] = useState<string>("");
  const [status, setStatus] = useState<FormStatus>("idle");

  const active = AUDIENCES.find((a) => a.id === audience) ?? AUDIENCES[0];

  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
    setEmail(event.target.value);
    if (status === "error") setStatus("idle");
  };

  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    if (!EMAIL_RE.test(email.trim())) {
      setStatus("error");
      return;
    }
    setStatus("success");
  };

  const resetForm = () => {
    setStatus("idle");
    setEmail("");
  };

  return (
    <section className="relative w-full overflow-hidden bg-gradient-to-b from-white via-indigo-50/60 to-white px-6 py-24 sm:py-32 dark:from-slate-950 dark:via-indigo-950/40 dark:to-slate-950">
      <style>{HXCG_CSS}</style>

      <div aria-hidden="true" className="pointer-events-none absolute inset-0">
        <div className="hxcg-grid absolute inset-0" />
        <div className="hxcg-halo absolute left-1/2 top-[-20%] h-[44rem] w-[44rem] rounded-full opacity-50 blur-2xl dark:opacity-40" />
        <div className="hxcg-orb-a absolute -left-16 top-24 h-72 w-72 rounded-full bg-gradient-to-br from-indigo-400/40 to-sky-400/30 blur-3xl dark:from-indigo-500/30 dark:to-sky-500/20" />
        <div className="hxcg-orb-b absolute -right-12 top-40 h-80 w-80 rounded-full bg-gradient-to-br from-violet-400/40 to-rose-400/25 blur-3xl dark:from-violet-500/30 dark:to-rose-500/20" />
        <div className="hxcg-orb-c absolute -bottom-24 left-1/3 h-72 w-72 rounded-full bg-gradient-to-br from-emerald-300/30 to-sky-400/25 blur-3xl dark:from-emerald-500/20 dark:to-sky-500/20" />
      </div>

      <motion.div
        variants={container}
        initial={prefersReducedMotion ? false : "hidden"}
        animate="show"
        className="relative mx-auto flex max-w-3xl flex-col items-center text-center"
      >
        <motion.a
          variants={item}
          href="#changelog"
          className="group inline-flex items-center gap-2 rounded-full border border-slate-200/80 bg-white/70 px-3 py-1 text-sm font-medium text-slate-700 shadow-sm backdrop-blur transition hover:border-indigo-300 hover:text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:border-white/10 dark:bg-white/5 dark:text-slate-300 dark:hover:text-white dark:focus-visible:ring-offset-slate-950"
        >
          <span className="relative flex h-2 w-2">
            <span className="hxcg-dot absolute inline-flex h-full w-full rounded-full bg-emerald-400" />
            <span className="relative inline-flex h-2 w-2 rounded-full bg-emerald-500" />
          </span>
          Northwind Automations 2.0 is live
          <svg
            viewBox="0 0 16 16"
            aria-hidden="true"
            className="h-3.5 w-3.5 transition-transform group-hover:translate-x-0.5"
            fill="none"
            stroke="currentColor"
            strokeWidth="1.6"
            strokeLinecap="round"
            strokeLinejoin="round"
          >
            <path d="M4 8h8" />
            <path d="M8.5 4.5 12 8l-3.5 3.5" />
          </svg>
        </motion.a>

        <motion.h1
          variants={item}
          className="mt-6 text-4xl font-semibold tracking-tight text-slate-900 sm:text-6xl dark:text-white"
        >
          Ship the work that used to take weeks{" "}
          <span className="bg-gradient-to-r from-indigo-600 via-violet-600 to-sky-600 bg-clip-text text-transparent dark:from-indigo-300 dark:via-violet-300 dark:to-sky-300">
            in a single afternoon
          </span>
        </motion.h1>

        <motion.p
          variants={item}
          className="mt-6 max-w-2xl text-lg leading-relaxed text-slate-600 dark:text-slate-300"
        >
          Northwind connects the tools you already use, automates the busywork, and
          gives every team one calm place to move projects forward — no more status
          meetings or scattered spreadsheets.
        </motion.p>

        <motion.div variants={item} className="mt-8 flex flex-col items-center gap-3">
          <div
            role="group"
            aria-label="Choose your team type"
            className="inline-flex items-center gap-1 rounded-full border border-slate-200 bg-white/70 p-1 backdrop-blur dark:border-white/10 dark:bg-white/5"
          >
            {AUDIENCES.map((option) => {
              const selected = option.id === audience;
              return (
                <button
                  key={option.id}
                  type="button"
                  onClick={() => setAudience(option.id)}
                  aria-pressed={selected}
                  className={`relative rounded-full px-4 py-1.5 text-sm font-medium transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 ${
                    selected
                      ? "text-slate-900 dark:text-white"
                      : "text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
                  }`}
                >
                  {selected && (
                    <motion.span
                      layoutId="hxcg-seg"
                      transition={{
                        duration: prefersReducedMotion ? 0 : 0.3,
                        ease: "easeOut",
                      }}
                      className="absolute inset-0 rounded-full bg-white shadow-sm dark:bg-slate-800"
                    />
                  )}
                  <span className="relative z-10">{option.label}</span>
                </button>
              );
            })}
          </div>
          <p
            aria-live="polite"
            className="min-h-[1.5rem] max-w-md text-sm text-slate-500 dark:text-slate-400"
          >
            {active.blurb}
          </p>
        </motion.div>

        <motion.div variants={item} className="mt-10 w-full max-w-md">
          {status === "success" ? (
            <div
              role="status"
              className="flex items-center gap-3 rounded-2xl border border-emerald-200 bg-emerald-50 px-5 py-4 text-left dark:border-emerald-500/30 dark:bg-emerald-500/10"
            >
              <svg
                viewBox="0 0 24 24"
                aria-hidden="true"
                className="h-6 w-6 shrink-0 text-emerald-600 dark:text-emerald-400"
                fill="none"
                stroke="currentColor"
                strokeWidth="2"
                strokeLinecap="round"
                strokeLinejoin="round"
              >
                <circle cx="12" cy="12" r="9" />
                <path d="m8.5 12 2.5 2.5 4.5-5" />
              </svg>
              <div className="flex-1">
                <p className="text-sm font-semibold text-emerald-800 dark:text-emerald-200">
                  You&apos;re on the list.
                </p>
                <p className="text-sm text-emerald-700/80 dark:text-emerald-300/70">
                  A workspace invite is on its way to {email}.
                </p>
              </div>
              <button
                type="button"
                onClick={resetForm}
                className="shrink-0 rounded-md text-sm font-medium text-emerald-700 underline underline-offset-2 transition 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"
              >
                Undo
              </button>
            </div>
          ) : (
            <form noValidate onSubmit={handleSubmit} className="flex flex-col gap-3 sm:flex-row">
              <div className="flex-1 text-left">
                <label htmlFor="hxcg-email" className="sr-only">
                  Work email
                </label>
                <input
                  id="hxcg-email"
                  name="email"
                  type="email"
                  autoComplete="email"
                  inputMode="email"
                  value={email}
                  onChange={handleChange}
                  placeholder="you@company.com"
                  aria-invalid={status === "error"}
                  aria-describedby={status === "error" ? "hxcg-email-error" : undefined}
                  className={`w-full rounded-xl border bg-white/80 px-4 py-3 text-sm text-slate-900 shadow-sm outline-none transition placeholder:text-slate-400 focus:ring-2 focus:ring-indigo-500/40 dark:bg-white/5 dark:text-white dark:placeholder:text-slate-500 ${
                    status === "error"
                      ? "border-rose-400 focus:border-rose-500 dark:border-rose-500/60"
                      : "border-slate-300 focus:border-indigo-500 dark:border-white/10"
                  }`}
                />
                {status === "error" && (
                  <p
                    id="hxcg-email-error"
                    role="alert"
                    className="mt-1.5 px-1 text-left text-sm text-rose-600 dark:text-rose-400"
                  >
                    Please enter a valid work email.
                  </p>
                )}
              </div>
              <button
                type="submit"
                className="inline-flex shrink-0 items-center justify-center gap-2 rounded-xl bg-gradient-to-r from-indigo-600 to-violet-600 px-5 py-3 text-sm font-semibold text-white shadow-sm 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 dark:focus-visible:ring-offset-slate-950"
              >
                Start free
                <svg
                  viewBox="0 0 16 16"
                  aria-hidden="true"
                  className="h-4 w-4"
                  fill="none"
                  stroke="currentColor"
                  strokeWidth="1.8"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                >
                  <path d="M3 8h10" />
                  <path d="M9 4l4 4-4 4" />
                </svg>
              </button>
            </form>
          )}
          <p className="mt-3 text-xs text-slate-500 dark:text-slate-400">
            Free 14-day trial · No credit card required
          </p>
        </motion.div>

        <motion.ul
          variants={item}
          className="mt-12 flex flex-wrap items-center justify-center gap-x-8 gap-y-3 text-sm text-slate-500 dark:text-slate-400"
        >
          <li className="inline-flex items-center gap-2">
            <svg
              viewBox="0 0 24 24"
              aria-hidden="true"
              className="h-4 w-4 text-indigo-500 dark:text-indigo-400"
              fill="none"
              stroke="currentColor"
              strokeWidth="1.8"
              strokeLinecap="round"
              strokeLinejoin="round"
            >
              <path d="M16 20v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
              <circle cx="9" cy="7" r="3.2" />
              <path d="M22 20v-2a4 4 0 0 0-3-3.87" />
              <path d="M16 3.5a4 4 0 0 1 0 7" />
            </svg>
            Trusted by 4,200+ teams
          </li>
          <li className="inline-flex items-center gap-2">
            <svg
              viewBox="0 0 24 24"
              aria-hidden="true"
              className="h-4 w-4 text-emerald-500 dark:text-emerald-400"
              fill="none"
              stroke="currentColor"
              strokeWidth="1.8"
              strokeLinecap="round"
              strokeLinejoin="round"
            >
              <path d="M12 3 5 6v5c0 4.5 3 7.7 7 9 4-1.3 7-4.5 7-9V6l-7-3Z" />
              <path d="m9 12 2 2 4-4" />
            </svg>
            SOC 2 Type II
          </li>
          <li className="inline-flex items-center gap-2">
            <svg
              viewBox="0 0 24 24"
              aria-hidden="true"
              className="h-4 w-4 text-amber-500 dark:text-amber-400"
              fill="none"
              stroke="currentColor"
              strokeWidth="1.8"
              strokeLinecap="round"
              strokeLinejoin="round"
            >
              <path d="M13 2 4 14h6l-1 8 9-12h-6l1-8Z" />
            </svg>
            99.99% uptime
          </li>
        </motion.ul>
      </motion.div>
    </section>
  );
}

Dependencies

motion

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 quote

Similar components

Browse all →
Spotlight Hero

Spotlight Hero

Original

A centred hero with a soft radial spotlight, badge and dual call-to-action.

Split Hero

Split Hero

Original

A two-column hero pairing a headline and CTAs with a product mock and social proof.

Gradient Spotlight Hero

Gradient Spotlight Hero

Original

A minimal centred hero with a soft gradient-mesh backdrop, announcement pill and dual call-to-action buttons.

App Preview Hero

App Preview Hero

Original

A centred hero that pairs headline copy with a realistic product dashboard mock built entirely from markup, complete with browser chrome and a floating notification card.

Waitlist Capture Hero

Waitlist Capture Hero

Original

A dark, focused hero with an inline email capture form and avatar social proof, ready for pre-launch waitlists.

Image Backdrop Hero

Image Backdrop Hero

Original

A cinematic full-bleed hero with a layered image-style backdrop, overlaid left-aligned copy and a trusted-by logos strip.

Gradient Mesh Hero with Staggered Text Reveal

Gradient Mesh Hero with Staggered Text Reveal

Original

A full-screen hero pairing a drifting animated gradient-mesh backdrop with a word-by-word staggered blur-in headline and a logo marquee under the fold.

Typewriter Rotating Words Hero

Typewriter Rotating Words Hero

Original

A hero whose headline cycles through rotating words with a live typewriter caret over an animated grid backdrop, degrading to a gentle word swap when reduced motion is on.

Floating UI Cards Hero with Mouse Parallax

Floating UI Cards Hero with Mouse Parallax

Original

A hero with glassy floating UI cards that drift continuously and shift on mouse-parallax depth around a staggered centre headline.

Aurora Hero with Shimmer CTA and Logo Marquee

Aurora Hero with Shimmer CTA and Logo Marquee

Original

A dark hero with animated aurora light beams, a shimmering sweep across the primary CTA, and dual-direction logo marquees beneath the fold.

Gradient Conversion Hero

Gradient Conversion Hero

MIT

Centered, full-screen marketing hero with a colour-accent headline word, supporting copy, and dual primary/secondary CTA buttons. Dark-mode ready and dependency-free.

Split Showcase Hero

Split Showcase Hero

MIT

Two-column hero on a dark background with a headline, paragraph, and two buttons beside a gradient media panel. Responsive column stack on mobile.