Hero 1: split with product visual
Split hero: badge, word-by-word headline, two calls to action and proof points on the left, a live product summary card on the right, over an animated grid.
// Ballmac UI: Hero 1. https://ui.ballmac.com/blocks/hero-1
import * as React from "react"
import { ArrowRight, Check } from "lucide-react"
import { AnimatedGrid } from "@/components/ballmac/animated-grid"
import { Badge } from "@/components/ballmac/badge"
import { buttonVariants } from "@/components/ballmac/button"
import { NumberTicker } from "@/components/ballmac/number-ticker"
import { TextReveal } from "@/components/ballmac/text-reveal"
import { cn } from "@/lib/utils"
import { Media, type MediaSource } from "@/components/ballmac/media"
type Action = { label: string; href: string }
type Hero1Props = Omit<React.ComponentProps<"section">, "title"> & {
/** Short label above the headline, e.g. a release note. */
eyebrow?: string
/** The headline. Revealed word by word. */
title?: string
/** One or two sentences under the headline. */
description?: string
/** Main call to action. */
primaryAction?: Action
/** Secondary call to action. */
secondaryAction?: Action
/** Short proof points under the buttons. */
highlights?: string[]
/** Your product image instead of the sample deploy card. An image URL (give it mediaAlt), an object with alt text and a dark-mode file, or your own element. */
media?: MediaSource
/** Describes `media` when it is a plain URL. */
mediaAlt?: string
}
function Hero1({
eyebrow = "New · Deploy previews for every branch",
title = "Ship with confidence, not guesswork.",
description = "Every change gets a live preview, a performance budget and a rollback button. Your team reviews real behavior, not screenshots.",
primaryAction = { label: "Start free", href: "#" },
secondaryAction = { label: "Book a demo", href: "#" },
highlights = ["No credit card", "SOC 2 Type II", "Cancel anytime"],
media,
mediaAlt,
className,
...props
}: Hero1Props) {
return (
<section data-slot="hero-1" className={cn("relative isolate overflow-hidden", className)} {...props}>
<AnimatedGrid className="absolute inset-0 -z-10 [mask-image:radial-gradient(ellipse_70%_60%_at_30%_30%,#000_30%,transparent_75%)]" />
<div className="mx-auto grid max-w-6xl items-center gap-14 px-4 py-20 sm:px-6 md:py-28 lg:grid-cols-[1.05fr_1fr]">
<div>
<Badge variant="outline" className="rounded-full px-3 py-1">
{eyebrow}
</Badge>
<TextReveal as="h1" className="mt-6 text-4xl font-semibold tracking-[-0.04em] text-balance sm:text-5xl lg:text-6xl">
{title}
</TextReveal>
<p className="mt-6 max-w-xl text-lg leading-relaxed text-pretty text-muted-foreground">{description}</p>
<div className="mt-8 flex flex-wrap gap-3">
<a className={buttonVariants({ size: "lg", shape: "pill" })} href={primaryAction.href}>
{primaryAction.label} <ArrowRight className="rtl:rotate-180"/>
</a>
<a className={buttonVariants({ variant: "outline", size: "lg", shape: "pill" })} href={secondaryAction.href}>{secondaryAction.label}</a>
</div>
<ul className="mt-8 flex flex-wrap gap-x-6 gap-y-2 text-sm text-muted-foreground">
{highlights.map((h) => (
<li key={h} className="flex items-center gap-2">
<Check className="size-4 text-foreground" aria-hidden="true" />
{h}
</li>
))}
</ul>
</div>
<Media media={media} alt={mediaAlt} frame priority fallback={<HeroVisual />} />
</div>
</section>
)
}
/** Product visual: a deploy summary card built from Ballmac components. */
function HeroVisual() {
const stats = [
{ label: "Previews this week", value: 1284 },
{ label: "Median build", value: 42, suffix: "s" },
{ label: "Rollbacks avoided", value: 17 },
]
return (
<div className="relative rounded-2xl border bg-card p-6 shadow-[0_30px_80px_-40px_rgb(0_0_0/0.35)] sm:p-8">
<div className="flex items-center justify-between gap-4">
<div>
<p className="font-mono text-xs text-muted-foreground">feat/checkout-v2</p>
<p className="mt-1 font-medium">Preview ready</p>
</div>
<Badge status="success">Passing</Badge>
</div>
<dl className="mt-8 grid grid-cols-3 gap-px overflow-hidden rounded-xl border bg-border">
{stats.map((s) => (
<div key={s.label} className="bg-card p-4">
<dt className="text-xs text-muted-foreground">{s.label}</dt>
<dd className="mt-2 text-2xl font-semibold tracking-tight">
<NumberTicker value={s.value} />
{s.suffix}
</dd>
</div>
))}
</dl>
<ol className="mt-6 space-y-3 text-sm">
{["Build", "Tests", "Performance budget", "Preview deployed"].map((step) => (
<li key={step} className="flex items-center justify-between rounded-lg border px-3 py-2">
<span>{step}</span>
<Check className="size-4 text-chart-2" aria-label="Done" />
</li>
))}
</ol>
</div>
)
}
export { Hero1, type Hero1Props }Installation
$ pnpm dlx shadcn@latest add @ballmac/hero-1Install the dependencies.
$ pnpm add lucide-reactAdd the Ballmac items it builds on.
$ pnpm dlx shadcn@latest add @ballmac/animated-grid @ballmac/badge @ballmac/button @ballmac/number-ticker @ballmac/text-reveal @ballmac/mediaCopy the source into your project.
components/ballmac/blocks/hero-1/hero-1.tsx// Ballmac UI: Hero 1. https://ui.ballmac.com/blocks/hero-1 import * as React from "react" import { ArrowRight, Check } from "lucide-react" import { AnimatedGrid } from "@/components/ballmac/animated-grid" import { Badge } from "@/components/ballmac/badge" import { buttonVariants } from "@/components/ballmac/button" import { NumberTicker } from "@/components/ballmac/number-ticker" import { TextReveal } from "@/components/ballmac/text-reveal" import { cn } from "@/lib/utils" import { Media, type MediaSource } from "@/components/ballmac/media" type Action = { label: string; href: string } type Hero1Props = Omit<React.ComponentProps<"section">, "title"> & { /** Short label above the headline, e.g. a release note. */ eyebrow?: string /** The headline. Revealed word by word. */ title?: string /** One or two sentences under the headline. */ description?: string /** Main call to action. */ primaryAction?: Action /** Secondary call to action. */ secondaryAction?: Action /** Short proof points under the buttons. */ highlights?: string[] /** Your product image instead of the sample deploy card. An image URL (give it mediaAlt), an object with alt text and a dark-mode file, or your own element. */ media?: MediaSource /** Describes `media` when it is a plain URL. */ mediaAlt?: string } function Hero1({ eyebrow = "New · Deploy previews for every branch", title = "Ship with confidence, not guesswork.", description = "Every change gets a live preview, a performance budget and a rollback button. Your team reviews real behavior, not screenshots.", primaryAction = { label: "Start free", href: "#" }, secondaryAction = { label: "Book a demo", href: "#" }, highlights = ["No credit card", "SOC 2 Type II", "Cancel anytime"], media, mediaAlt, className, ...props }: Hero1Props) { return ( <section data-slot="hero-1" className={cn("relative isolate overflow-hidden", className)} {...props}> <AnimatedGrid className="absolute inset-0 -z-10 [mask-image:radial-gradient(ellipse_70%_60%_at_30%_30%,#000_30%,transparent_75%)]" /> <div className="mx-auto grid max-w-6xl items-center gap-14 px-4 py-20 sm:px-6 md:py-28 lg:grid-cols-[1.05fr_1fr]"> <div> <Badge variant="outline" className="rounded-full px-3 py-1"> {eyebrow} </Badge> <TextReveal as="h1" className="mt-6 text-4xl font-semibold tracking-[-0.04em] text-balance sm:text-5xl lg:text-6xl"> {title} </TextReveal> <p className="mt-6 max-w-xl text-lg leading-relaxed text-pretty text-muted-foreground">{description}</p> <div className="mt-8 flex flex-wrap gap-3"> <a className={buttonVariants({ size: "lg", shape: "pill" })} href={primaryAction.href}> {primaryAction.label} <ArrowRight className="rtl:rotate-180"/> </a> <a className={buttonVariants({ variant: "outline", size: "lg", shape: "pill" })} href={secondaryAction.href}>{secondaryAction.label}</a> </div> <ul className="mt-8 flex flex-wrap gap-x-6 gap-y-2 text-sm text-muted-foreground"> {highlights.map((h) => ( <li key={h} className="flex items-center gap-2"> <Check className="size-4 text-foreground" aria-hidden="true" /> {h} </li> ))} </ul> </div> <Media media={media} alt={mediaAlt} frame priority fallback={<HeroVisual />} /> </div> </section> ) } /** Product visual: a deploy summary card built from Ballmac components. */ function HeroVisual() { const stats = [ { label: "Previews this week", value: 1284 }, { label: "Median build", value: 42, suffix: "s" }, { label: "Rollbacks avoided", value: 17 }, ] return ( <div className="relative rounded-2xl border bg-card p-6 shadow-[0_30px_80px_-40px_rgb(0_0_0/0.35)] sm:p-8"> <div className="flex items-center justify-between gap-4"> <div> <p className="font-mono text-xs text-muted-foreground">feat/checkout-v2</p> <p className="mt-1 font-medium">Preview ready</p> </div> <Badge status="success">Passing</Badge> </div> <dl className="mt-8 grid grid-cols-3 gap-px overflow-hidden rounded-xl border bg-border"> {stats.map((s) => ( <div key={s.label} className="bg-card p-4"> <dt className="text-xs text-muted-foreground">{s.label}</dt> <dd className="mt-2 text-2xl font-semibold tracking-tight"> <NumberTicker value={s.value} /> {s.suffix} </dd> </div> ))} </dl> <ol className="mt-6 space-y-3 text-sm"> {["Build", "Tests", "Performance budget", "Preview deployed"].map((step) => ( <li key={step} className="flex items-center justify-between rounded-lg border px-3 py-2"> <span>{step}</span> <Check className="size-4 text-chart-2" aria-label="Done" /> </li> ))} </ol> </div> ) } export { Hero1, type Hero1Props }Update the import paths to match your project setup.
Usage
import { Hero1 } from "@/components/ballmac/blocks/hero-1/hero-1"
export default function Hero1Demo() {
return <Hero1 />
}Dependencies
- npm
- lucide-react
Use with AI
The top section of a SaaS or product landing page. Edit the props for copy and swap HeroVisual for your own product screenshot or component. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Hero 1: split with product visual (@ballmac/hero-1) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Landing pages with a product to show
- Launch pages that need a clear primary action
Not for
- Developer tools where the install command is the hero (use hero-2)
- AI products where a live prompt sells best (use hero-3)
Registry JSON: https://ui.ballmac.com/r/hero-1.json
Free to use in personal and commercial projects.
More hero blocks
Hero 2: developer tool with install command
Centered hero for developer tools: headline, two actions, package-manager install tabs and a terminal window with a light beam tracing its border.
Hero 3: AI product with live prompt
Hero for AI products: a real prompt input on the left, and on the right a mock answer with a reasoning panel, a tool call and a streaming reply.
Hero 4: split with interactive globe
Split hero with a live pill, headline, two actions and headline stats beside a large draggable WebGL globe marking your regions.
Hero 5: Mac app with opening laptop
Centered hero for a Mac app: shiny highlighted headline, download button and requirements, over a laptop whose lid opens into view on a live app window.