← HeroOpen in v0
Hero 01 — Spotlight
Centered hero with cursor spotlight, gradient headline, dual CTAs and a CSS-only product mock.
$ npx shadcn@latest add @ludusvibe/hero-01Code
Installed to components/sections/hero-01.tsx — it's yours to edit.
"use client"
import * as React from "react"
import { motion } from "motion/react"
import { ArrowRight } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import { buttonVariants } from "@/components/ui/button"
import { Spotlight } from "@/components/ludus/spotlight"
import { fadeUp, scaleIn, stagger, viewport } from "@/lib/motion"
import { cn } from "@/lib/utils"
type Cta = { label: string; href: string }
export type Hero01Props = {
eyebrow?: string
title?: React.ReactNode
description?: string
primaryCta?: Cta
secondaryCta?: Cta
/** Heading element — use "h1" when this is the page's main heading. */
as?: "h1" | "h2"
className?: string
}
export function Hero01({
eyebrow = "Now in public beta",
title = (
<>
Ship landing pages{" "}
<span className="bg-gradient-to-r from-primary via-primary/80 to-foreground bg-clip-text text-transparent">
at the speed of thought
</span>
</>
),
description = "Production-ready, animated sections that drop straight into your codebase and inherit your brand automatically. No design debt, no lock-in — you own the code.",
primaryCta = { label: "Start building", href: "#" },
secondaryCta = { label: "Browse sections", href: "#" },
as: Heading = "h1",
className,
}: Hero01Props) {
return (
<section className={cn("relative overflow-hidden bg-background", className)}>
<div
aria-hidden
className="absolute inset-0 [background-image:linear-gradient(to_right,var(--border)_1px,transparent_1px),linear-gradient(to_bottom,var(--border)_1px,transparent_1px)] [background-size:64px_64px] [mask-image:radial-gradient(ellipse_60%_50%_at_50%_0%,black,transparent)]"
/>
<div
aria-hidden
className="absolute -top-40 left-1/2 h-[480px] w-[720px] -translate-x-1/2 rounded-full bg-primary/20 blur-[140px]"
/>
<Spotlight />
<motion.div
initial="hidden"
whileInView="visible"
viewport={viewport}
variants={stagger()}
className="relative mx-auto flex max-w-5xl flex-col items-center px-6 pt-28 pb-24 text-center md:pt-40"
>
<motion.div variants={fadeUp}>
<Badge
variant="outline"
className="gap-2 rounded-full px-3 py-1 text-sm font-normal text-muted-foreground"
>
<span className="size-1.5 animate-pulse rounded-full bg-primary" />
{eyebrow}
</Badge>
</motion.div>
<motion.div variants={fadeUp}>
<Heading className="mt-6 max-w-3xl text-balance font-heading text-5xl font-semibold tracking-tighter sm:text-6xl md:text-7xl">
{title}
</Heading>
</motion.div>
<motion.p
variants={fadeUp}
className="mt-6 max-w-2xl text-lg text-pretty text-muted-foreground"
>
{description}
</motion.p>
<motion.div
variants={fadeUp}
className="mt-10 flex flex-col items-center gap-3 sm:flex-row"
>
<a
href={primaryCta.href}
className={cn(buttonVariants({ size: "lg" }), "group")}
>
{primaryCta.label}
<ArrowRight className="transition-transform group-hover:translate-x-0.5" />
</a>
<a
href={secondaryCta.href}
className={buttonVariants({ size: "lg", variant: "outline" })}
>
{secondaryCta.label}
</a>
</motion.div>
{/* CSS-only product mock */}
<motion.div variants={scaleIn} className="relative mt-20 w-full">
<div
aria-hidden
className="absolute -inset-x-8 top-8 -bottom-8 rounded-[2rem] bg-primary/10 blur-2xl"
/>
<div className="relative overflow-hidden rounded-2xl border bg-card shadow-2xl">
<div className="flex items-center gap-1.5 border-b px-4 py-3">
<span className="size-2.5 rounded-full bg-muted-foreground/25" />
<span className="size-2.5 rounded-full bg-muted-foreground/25" />
<span className="size-2.5 rounded-full bg-muted-foreground/25" />
<div className="mx-auto h-5 w-64 max-w-[50%] rounded-md bg-muted" />
</div>
<div className="grid grid-cols-[180px_1fr] max-sm:grid-cols-1">
<div className="hidden flex-col gap-2 border-r p-4 sm:flex">
<div className="h-7 rounded-md bg-primary/15" />
<div className="h-7 rounded-md bg-muted" />
<div className="h-7 rounded-md bg-muted" />
<div className="h-7 rounded-md bg-muted" />
<div className="h-7 rounded-md bg-muted" />
</div>
<div className="flex flex-col gap-4 p-6">
<div className="flex gap-4">
{[0, 1, 2].map((i) => (
<div key={i} className="h-20 flex-1 rounded-lg border bg-background p-3">
<div className="h-3 w-1/2 rounded bg-muted" />
<div className="mt-3 h-5 w-2/3 rounded bg-primary/30" />
</div>
))}
</div>
<div className="flex h-40 items-end gap-2 rounded-lg border bg-background p-4">
{[40, 65, 45, 80, 55, 90, 70, 100, 60, 85, 75, 95].map((h, i) => (
<div
key={i}
style={{ height: `${h}%` }}
className="flex-1 rounded-t bg-primary/60"
/>
))}
</div>
</div>
</div>
<div
aria-hidden
className="pointer-events-none absolute inset-x-0 bottom-0 h-24 bg-gradient-to-t from-background to-transparent"
/>
</div>
</motion.div>
</motion.div>
</section>
)
}