Innovations
heroes /

Product Cutout Hero (Fashion / Ecom)

Bold-color hero with a full-bleed cutout product or model photo, big serif headline with script accent (Caveat), and a two-tone offer badge floating bottom-right. Built from a

Preview

Source

tsx
import { ArrowRight, MoreHorizontal } from "lucide-react";

export interface ProductCutoutProps {
  brand?: string;
  eyebrow?: string;
  headlinePart1?: string;
  headlinePart2?: string;
  scriptHighlight?: string; // big italic script word, e.g. "Soul"
  description?: string;
  primaryCta?: { label: string; href: string };
  websiteUrl?: string;
  /** Cutout product / model photo. Generate with the same bg color as bgColor for cutout illusion. */
  imageSrc?: string;
  imageAlt?: string;
  badgeOne?: { eyebrow: string; value: string };
  badgeTwo?: { eyebrow: string; value: string };
  /** Default = bold royal blue #1d6cf2 (matches generated model bg). */
  bgColor?: string;
  /** Default = bright accent orange #ff6b00 for CTA + badges. */
  accentColor?: string;
}

export default function ProductCutoutHero({
  brand = "TRADE",
  eyebrow = "Fashion",
  headlinePart1 = "SPIRIT OF",
  headlinePart2 = "YOUR",
  scriptHighlight = "Soul",
  description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed diam nonumy nibh euismod tincidunt.",
  primaryCta = { label: "LEARN MORE", href: "#shop" },
  websiteUrl = "www.website.com",
  imageSrc = "/heroes/product-cutout/model.webp?v=2",
  imageAlt = "Featured product",
  badgeOne = { eyebrow: "BEST", value: "OFFER" },
  badgeTwo = { eyebrow: "DISCOUNT", value: "30% OFF" },
  bgColor = "#1d6cf2",
  accentColor = "#ff6b00",
}: ProductCutoutProps) {
  return (
    <>
      {/* Caveat for the script highlight */}
      <link rel="preconnect" href="https://fonts.googleapis.com" />
      <link
        rel="stylesheet"
        href="https://fonts.googleapis.com/css2?family=Caveat:wght@600;700&display=swap"
      />
      <section
        className="relative min-h-[640px] overflow-hidden text-white"
        style={{ background: bgColor }}
      >
        {/* Soft radial gradient lightener behind the model — gives the
            background a circle of slightly-lighter blue that fades into
            the base color around it. */}
        <div
          aria-hidden
          className="pointer-events-none absolute inset-0"
          style={{
            background:
              "radial-gradient(circle at 72% 55%, rgba(255,255,255,0.22) 0%, rgba(255,255,255,0.10) 28%, transparent 55%)",
          }}
        />

        {/* Decorative dots scattered */}
        <div aria-hidden className="pointer-events-none absolute inset-0">
          <span className="absolute top-[18%] left-[25%] h-1.5 w-1.5 rounded-full bg-white/40" />
          <span className="absolute top-[35%] left-[12%] h-2 w-2 rounded-full bg-white/30" />
          <span className="absolute top-[8%] right-[35%] h-1.5 w-1.5 rounded-full bg-white/40" />
          <span className="absolute bottom-[25%] left-[8%] h-2 w-2 rounded-full bg-white/30" />
          <span className="absolute bottom-[10%] right-[12%] h-1.5 w-1.5 rounded-full bg-white/40" />
        </div>

        <div className="relative mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
          {/* Top bar */}
          <div className="flex items-center justify-between py-6">
            <a href="/" className="flex items-center gap-2">
              <BrandIcon />
              <span className="text-xl font-bold tracking-wide text-white">
                {brand}
              </span>
            </a>
            <button
              type="button"
              aria-label="Menu"
              className="rounded-full p-2 text-white/80 transition-colors hover:bg-white/10"
            >
              <MoreHorizontal className="h-5 w-5" />
            </button>
          </div>

          {/* Hero grid — image column is bottom-anchored, copy column stays centered */}
          <div className="relative grid items-end gap-8 pt-8 lg:grid-cols-[1fr_1.1fr] lg:gap-4 lg:pt-12">
            {/* Left: copy (vertically centered + bottom padding) */}
            <div className="relative z-10 max-w-md self-center pb-16 lg:pb-24">
              <p className="mb-3 text-sm font-medium tracking-wider uppercase text-white/70">
                {eyebrow}
              </p>
              <h1
                className="font-extrabold uppercase leading-[0.95] tracking-tight text-white"
                style={{ fontSize: "clamp(2.75rem, 6vw, 4.5rem)" }}
              >
                {headlinePart1}
                <br />
                {headlinePart2}{" "}
                <span
                  className="inline-block italic normal-case"
                  style={{
                    fontFamily: "'Caveat', cursive",
                    fontWeight: 700,
                    color: accentColor,
                    fontSize: "1.05em",
                    transform: "translateY(0.05em)",
                  }}
                >
                  {scriptHighlight}
                </span>
              </h1>
              <p className="mt-6 max-w-sm text-sm leading-relaxed text-white/80">
                {description}
              </p>
              <a
                href={primaryCta.href}
                className="mt-7 inline-flex items-center gap-2 rounded-full px-7 py-3 text-xs font-bold uppercase tracking-wider text-white shadow-lg transition-all hover:-translate-y-0.5"
                style={{ background: accentColor, boxShadow: `0 8px 22px ${accentColor}55` }}
              >
                {primaryCta.label}
                <ArrowRight className="h-4 w-4" />
              </a>
              <p className="mt-12 text-xs text-white/60">{websiteUrl}</p>
            </div>

            {/* Right: cutout product image — bottom-anchored, fills column height */}
            <div className="relative self-end flex justify-center items-end min-h-[560px] lg:min-h-[640px]">
              <img
                src={imageSrc}
                alt={imageAlt}
                width={900}
                height={1400}
                loading="eager"
                fetchPriority="high"
                decoding="async"
                className="relative z-10 block h-full max-h-[640px] lg:max-h-[720px] w-auto object-contain object-bottom"
              />

              {/* Offer badges card — bottom-right, two-tone */}
              <div className="absolute bottom-0 right-0 z-20 flex overflow-hidden rounded-2xl bg-white shadow-2xl">
                <div
                  className="flex flex-col justify-center px-5 py-4 text-white"
                  style={{ background: accentColor }}
                >
                  <span className="text-xs font-bold uppercase tracking-wider opacity-90">
                    {badgeOne.eyebrow}
                  </span>
                  <span className="text-2xl font-extrabold leading-tight">
                    {badgeOne.value}
                  </span>
                </div>
                <div className="flex flex-col justify-center px-5 py-4">
                  <span
                    className="text-xs font-bold uppercase tracking-wider"
                    style={{ color: accentColor }}
                  >
                    {badgeTwo.eyebrow}
                  </span>
                  <span className="text-2xl font-extrabold leading-tight text-slate-900">
                    {badgeTwo.value}
                  </span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </section>
    </>
  );
}

function BrandIcon() {
  return (
    <svg viewBox="0 0 24 24" className="h-6 w-6" fill="none" aria-hidden>
      <path
        d="M4 4 L 12 12 L 4 20 L 12 12 L 20 4 L 12 12 L 20 20"
        stroke="currentColor"
        strokeWidth="2"
        strokeLinecap="round"
        strokeLinejoin="round"
        className="text-white"
      />
      <circle cx="12" cy="12" r="2" fill="currentColor" className="text-white" />
    </svg>
  );
}
Claude Code Instructions

CLI Install

npx innovations add product-cutout

Where to use it

Use this for ecommerce, fashion, beauty, lifestyle products, drops — anywhere you've got a great product/model shot and want to lead with the offer. In Astro: --- import ProductCutoutHero from '../components/innovations/heroes/product-cutout'; --- <ProductCutoutHero /> In Next.js: import ProductCutoutHero from '@/components/innovations/heroes/product-cutout'; No client:* required — pure presentation. CRITICAL — IMAGE PREP: The "cutout" effect works because the model photo's background is the SAME color as the section background (default royal blue #1d6cf2). When generating a replacement product photo: - Use a solid background that matches bgColor exactly - OR use a transparent-PNG cutout (if your image tool supports it) - object-contain ensures the figure isn't cropped If the photo background doesn't match, you'll see a visible square edge. CUSTOMIZATION: <ProductCutoutHero brand="YOUR BRAND" eyebrow="Collection" headlinePart1="MAKE IT" headlinePart2="YOUR" scriptHighlight="Story" /* big Caveat script accent */ description="..." primaryCta={{ label: "SHOP NOW", href: "/shop" }} websiteUrl="www.yourbrand.com" imageSrc="/your-product.webp" badgeOne={{ eyebrow: "NEW", value: "DROP" }} badgeTwo={{ eyebrow: "FREE", value: "SHIPPING" }} bgColor="#1d6cf2" accentColor="#ff6b00" /> PALETTE: blue + orange. Both are exposed as props. The script accent uses accentColor. FONT: Caveat shipped via baked-in Google Fonts <link>.