Skip to content
Black FridayBlack Friday launch price: Lifetime $149 until 30 Nov. Regular price $299 from 1 Dec.ends in 15d 03:13:38Get Lifetime →
loopkit
freeNew125 lines · framer-motion

Hero Centered Badge

One promise, two buttons, no noise. A minimal centred SaaS hero: a badge pill, a bold headline, two CTAs and a row of generic trust chips, over a single slow-breathing ambient glow — the calmest hero in the set.

Use it for
Pages that want confidence over spectacle: a single clear value proposition with no chart, canvas or 3D scene competing for attention.
Themes
Light + dark
License
Commercial use
Live
New · Now with MCP

The calm way to ship AI products

A centred hero for teams who want confidence, not confetti: one clear promise, two clear actions.

No credit cardCancel anytimeSOC2-ready infra
tsx
"use client";

import * as React from "react";
import { motion, useReducedMotion } from "framer-motion";
import { cn } from "@/lib/utils";

function useReducedMotionSafe() {
  const prefersReduced = useReducedMotion();
  const [mounted, setMounted] = React.useState(false);
  React.useEffect(() => setMounted(true), []);
  return mounted ? !!prefersReduced : false;
}

/**
 * Minimal centred SaaS hero: a badge pill, a bold headline, two CTAs and a
 * row of generic trust chips, over a single slow-breathing ambient glow.
 * The calmest hero in the set — no canvas, no WebGL, no chart.
 */

export interface HeroCenteredBadgeProps {
  badge?: string;
  headline?: string;
  subcopy?: string;
  primaryCta?: string;
  secondaryCta?: string;
  /** Ambient glow strength (0.4–1.6). */
  glow?: number;
  /** Show the trust-chip row under the CTAs. */
  showChips?: boolean;
  className?: string;
}

const CHIPS = ["No credit card", "Cancel anytime", "SOC2-ready infra"];

export function HeroCenteredBadge({
  badge = "New · Now with MCP",
  headline = "The calm way to ship AI products",
  subcopy = "A centred hero for teams who want confidence, not confetti: one clear promise, two clear actions.",
  primaryCta = "Start free",
  secondaryCta = "Talk to sales",
  glow = 1,
  showChips = true,
  className,
}: HeroCenteredBadgeProps) {
  const reduce = useReducedMotionSafe();

  return (
    <section className={cn("relative isolate overflow-hidden bg-background", className)}>
      <div
        className="absolute inset-0 [background-image:radial-gradient(circle_at_1px_1px,var(--border)_1px,transparent_1px)] [background-size:32px_32px] opacity-[0.3] [mask-image:radial-gradient(ellipse_65%_55%_at_50%_30%,black,transparent_75%)]"
        aria-hidden
      />
      <motion.div
        className="absolute left-1/2 top-[22%] size-[26rem] -translate-x-1/2 rounded-full bg-[radial-gradient(circle,var(--signal),transparent_70%)] blur-3xl"
        style={{ opacity: 0.22 * glow }}
        animate={reduce ? undefined : { scale: [1, 1.12, 1], opacity: [0.18 * glow, 0.28 * glow, 0.18 * glow] }}
        transition={reduce ? undefined : { duration: 6, repeat: Infinity, ease: "easeInOut" }}
        aria-hidden
      />

      <div className="relative mx-auto flex min-h-[28rem] max-w-3xl flex-col items-center px-4 pb-16 pt-20 text-center sm:px-6 sm:pt-24">
        <motion.span
          initial={reduce ? false : { opacity: 0, y: -8 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.4 }}
          className="inline-flex items-center gap-2 rounded-full border border-border bg-card px-3 py-1.5 font-mono text-[11px] uppercase tracking-[0.16em] text-muted-foreground"
        >
          <span className="size-1.5 rounded-full bg-signal" aria-hidden />
          {badge}
        </motion.span>

        <motion.h1
          initial={reduce ? false : { opacity: 0, y: 16 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.55, delay: 0.1 }}
          className="mt-6 font-display text-[clamp(2.6rem,7vw,5.25rem)] font-extrabold leading-[0.96] tracking-[-0.045em]"
        >
          {headline}
        </motion.h1>

        <motion.p
          initial={reduce ? false : { opacity: 0, y: 10 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.5, delay: 0.25 }}
          className="mt-5 max-w-lg text-lg leading-relaxed text-muted-foreground"
        >
          {subcopy}
        </motion.p>

        <motion.div
          initial={reduce ? false : { opacity: 0, y: 10 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.5, delay: 0.35 }}
          className="mt-8 flex flex-wrap items-center justify-center gap-3"
        >
          <a href="#" className="inline-flex h-12 items-center rounded-full bg-primary px-6 text-[15px] font-semibold text-primary-foreground transition hover:opacity-90">
            {primaryCta} →
          </a>
          <a href="#" className="inline-flex h-12 items-center rounded-full border-2 border-foreground px-6 text-[15px] font-semibold transition hover:bg-foreground hover:text-background">
            {secondaryCta}
          </a>
        </motion.div>

        {showChips && (
          <motion.div
            initial={reduce ? false : { opacity: 0 }}
            animate={{ opacity: 1 }}
            transition={{ duration: 0.6, delay: 0.5 }}
            className="mt-12 flex flex-wrap items-center justify-center gap-2.5"
          >
            {CHIPS.map((c) => (
              <span key={c} className="inline-flex items-center gap-1.5 rounded-full bg-muted px-3 py-1.5 font-mono text-[11px] text-muted-foreground">
                <span className="size-1 rounded-full bg-ok" aria-hidden />
                {c}
              </span>
            ))}
          </motion.div>
        )}
      </div>
    </section>
  );
}

export default HeroCenteredBadge;

How it behaves

  • →A single radial-gradient glow sits behind the badge and headline, scaling and fading on a slow 6s breathing loop via framer-motion.
  • →A dot-grid texture fades out toward the edges (mask-image) to keep the composition anchored behind the text column.
  • →glow scales the ambient glow's base and peak opacity; showChips toggles a row of generic trust chips (no credit card, cancel anytime, SOC2-ready infra) below the CTAs.
  • →prefers-reduced-motion removes the breathing animation, leaving the glow static at its resting opacity.

Accessibility

  • ✓The glow and dot-grid are aria-hidden; only the badge, heading, copy, two real <a> CTAs and the trust chips are in the accessibility tree.
  • ✓Headline is a single <h1>; CTAs keep the design system's visible focus ring.
  • ✓No canvas/WebGL dependency at all — the lightest hero to render in the whole set.
  • ✓Honours prefers-reduced-motion.