Hero
Hero Split
Split title with a lead and action, two photos side by side and three figures with dividers.
Layout and motion from the Findit template — see it in contextimport * as React from "react";
import { FlipButton } from "@/components/ui/flip-button";
import { cn } from "@/lib/utils";
/*
* Hero Split — a split title (heading at the left, lead and action at the right), two photos side by side and three
* figures split by thin lines.
* Source: marcframe/findit → components/sections/AboutHeroSection.tsx (+ design-system SectionTitle split, FactList
* light).
*
* Layout kept from Framer: below 768px everything stacks; from 768px the lead block (up to 400px) sits beside the
* heading, aligned to its bottom, 48px apart (96px from 1024px). Photos share a row 10px apart, 30% and the rest, 200px
* tall (338px from 768px, 500px from 1024px). Figures stack with lines between them below 768px and sit in three
* columns with vertical lines from 768px. The button rolls its label (flip-button, spring bounce 0, 0.4s); nothing
* else moves. Dropped: Findit's dotted, uppercase label.
*/
export type HeroSplitFact = { value: string; title: string; text: string };
export type HeroSplitProps = {
/** Heading level: h1 when the hero opens the page. */
as?: "h1" | "h2";
/** Small line above the heading. */
label?: string;
title?: string;
lead?: string;
action?: { label: string; href: string };
/** Two photos: the narrow one first. */
images?: [{ src: string; alt: string }, { src: string; alt: string }];
facts?: HeroSplitFact[];
className?: string;
};
const photo = (id: string) => `https://images.unsplash.com/${id}?auto=format&fit=crop&w=1600&q=80`;
const defaults = {
label: "About us",
title: "Building dreams, one home at a time",
lead: "We're more than agents — we're partners who know the streets, the prices and the people, and stay with you long after the keys change hands.",
action: { label: "Contact us", href: "/contact" },
images: [
{ src: photo("photo-1484154218962-a197022b5858"), alt: "White kitchen with an island and black pendant lights" },
{ src: photo("photo-1600607687939-ce8a6c25118c"), alt: "Living room with a white sofa beside a wooden wall" },
] as [{ src: string; alt: string }, { src: string; alt: string }],
facts: [
{ value: "$150M+", title: "Properties sold", text: "Homes and investments closed for our clients since we opened." },
{ value: "1,200+", title: "Happy families", text: "Buyers and renters who found a place to call home." },
{ value: "20 yrs", title: "In the market", text: "Local knowledge in every neighbourhood we cover." },
],
};
export function HeroSplit({
as: Heading = "h1",
label = defaults.label,
title = defaults.title,
lead = defaults.lead,
action = defaults.action,
images = defaults.images,
facts = defaults.facts,
className,
}: HeroSplitProps) {
return (
<section className={cn("py-(--section-y)", className)}>
<div className="mx-auto flex max-w-(--container) flex-col gap-12 px-(--gutter)">
<div className="flex flex-col gap-5 md:flex-row md:items-end md:gap-12 lg:gap-24">
<div className="flex max-w-125 flex-col gap-2.5 md:flex-1">
{label && <p className="text-sm font-medium text-muted-foreground">{label}</p>}
<Heading className="font-heading text-h1 font-semibold tracking-tight text-balance">{title}</Heading>
</div>
<div className="flex max-w-100 flex-col items-start gap-5 md:flex-1">
{lead && <p className="text-lead text-pretty text-muted-foreground">{lead}</p>}
{action && <FlipButton href={action.href}>{action.label}</FlipButton>}
</div>
</div>
<div className="flex h-50 gap-2.5 md:h-84.5 lg:h-125">
<img src={images[0].src} alt={images[0].alt} className="h-full w-[30%] shrink-0 rounded-xl bg-muted object-cover" />
<img src={images[1].src} alt={images[1].alt} className="h-full min-w-0 flex-1 rounded-xl bg-muted object-cover" />
</div>
{facts.length > 0 && (
<div className="flex flex-col gap-5 md:flex-row">
{facts.map((fact, i) => (
<React.Fragment key={fact.value + fact.title}>
{i > 0 && <div aria-hidden className="h-px shrink-0 bg-border md:h-auto md:w-px" />}
<div className="flex flex-1 flex-col gap-5">
<p className="font-heading text-h1 font-semibold tracking-tight">{fact.value}</p>
<div className="flex flex-col gap-2.5">
<p className="font-medium">{fact.title}</p>
<p className="text-sm text-muted-foreground">{fact.text}</p>
</div>
</div>
</React.Fragment>
))}
</div>
)}
</div>
</section>
);
}Installation
$ npx shadcn@latest add https://framebrick.co/r/blocks/hero-split.json1. Add the Framebrick UI components it uses (they bring Motion).
npx shadcn@latest add https://framebrick.co/r/flip-button.json2. Add the block tokens to
app/globals.cssonce (theming).npx shadcn@latest add https://framebrick.co/r/blocks/tokens.json3. Copy the block into
components/hero-split.tsx.import * as React from "react"; import { FlipButton } from "@/components/ui/flip-button"; import { cn } from "@/lib/utils"; /* * Hero Split — a split title (heading at the left, lead and action at the right), two photos side by side and three * figures split by thin lines. * Source: marcframe/findit → components/sections/AboutHeroSection.tsx (+ design-system SectionTitle split, FactList * light). * * Layout kept from Framer: below 768px everything stacks; from 768px the lead block (up to 400px) sits beside the * heading, aligned to its bottom, 48px apart (96px from 1024px). Photos share a row 10px apart, 30% and the rest, 200px * tall (338px from 768px, 500px from 1024px). Figures stack with lines between them below 768px and sit in three * columns with vertical lines from 768px. The button rolls its label (flip-button, spring bounce 0, 0.4s); nothing * else moves. Dropped: Findit's dotted, uppercase label. */ export type HeroSplitFact = { value: string; title: string; text: string }; export type HeroSplitProps = { /** Heading level: h1 when the hero opens the page. */ as?: "h1" | "h2"; /** Small line above the heading. */ label?: string; title?: string; lead?: string; action?: { label: string; href: string }; /** Two photos: the narrow one first. */ images?: [{ src: string; alt: string }, { src: string; alt: string }]; facts?: HeroSplitFact[]; className?: string; }; const photo = (id: string) => `https://images.unsplash.com/${id}?auto=format&fit=crop&w=1600&q=80`; const defaults = { label: "About us", title: "Building dreams, one home at a time", lead: "We're more than agents — we're partners who know the streets, the prices and the people, and stay with you long after the keys change hands.", action: { label: "Contact us", href: "/contact" }, images: [ { src: photo("photo-1484154218962-a197022b5858"), alt: "White kitchen with an island and black pendant lights" }, { src: photo("photo-1600607687939-ce8a6c25118c"), alt: "Living room with a white sofa beside a wooden wall" }, ] as [{ src: string; alt: string }, { src: string; alt: string }], facts: [ { value: "$150M+", title: "Properties sold", text: "Homes and investments closed for our clients since we opened." }, { value: "1,200+", title: "Happy families", text: "Buyers and renters who found a place to call home." }, { value: "20 yrs", title: "In the market", text: "Local knowledge in every neighbourhood we cover." }, ], }; export function HeroSplit({ as: Heading = "h1", label = defaults.label, title = defaults.title, lead = defaults.lead, action = defaults.action, images = defaults.images, facts = defaults.facts, className, }: HeroSplitProps) { return ( <section className={cn("py-(--section-y)", className)}> <div className="mx-auto flex max-w-(--container) flex-col gap-12 px-(--gutter)"> <div className="flex flex-col gap-5 md:flex-row md:items-end md:gap-12 lg:gap-24"> <div className="flex max-w-125 flex-col gap-2.5 md:flex-1"> {label && <p className="text-sm font-medium text-muted-foreground">{label}</p>} <Heading className="font-heading text-h1 font-semibold tracking-tight text-balance">{title}</Heading> </div> <div className="flex max-w-100 flex-col items-start gap-5 md:flex-1"> {lead && <p className="text-lead text-pretty text-muted-foreground">{lead}</p>} {action && <FlipButton href={action.href}>{action.label}</FlipButton>} </div> </div> <div className="flex h-50 gap-2.5 md:h-84.5 lg:h-125"> <img src={images[0].src} alt={images[0].alt} className="h-full w-[30%] shrink-0 rounded-xl bg-muted object-cover" /> <img src={images[1].src} alt={images[1].alt} className="h-full min-w-0 flex-1 rounded-xl bg-muted object-cover" /> </div> {facts.length > 0 && ( <div className="flex flex-col gap-5 md:flex-row"> {facts.map((fact, i) => ( <React.Fragment key={fact.value + fact.title}> {i > 0 && <div aria-hidden className="h-px shrink-0 bg-border md:h-auto md:w-px" />} <div className="flex flex-1 flex-col gap-5"> <p className="font-heading text-h1 font-semibold tracking-tight">{fact.value}</p> <div className="flex flex-col gap-2.5"> <p className="font-medium">{fact.title}</p> <p className="text-sm text-muted-foreground">{fact.text}</p> </div> </div> </React.Fragment> ))} </div> )} </div> </section> ); }
Usage
import { HeroSplit } from "@/components/hero-split";
<HeroSplit
title="Building dreams, one home at a time"
images={[{ src: "/about/kitchen.jpg", alt: "…" }, { src: "/about/living.jpg", alt: "…" }]}
facts={[{ value: "$150M+", title: "Properties sold", text: "…" }]}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| as | "h1" | "h2" | "h1" | Heading level of the title. |
| label | string | About us | Small muted line above the heading. |
| title / lead | string | — | Heading (up to 500px) and the lead beside it from 768px (up to 400px). |
| action | { label, href } | Contact us | Rolling-label button under the lead. |
| images | [narrow, wide] — { src, alt } | — | Two photos in a row, 30% and the rest. |
| facts | { value, title, text }[] | 3 figures | Figures split by thin lines; [] hides them. |
| className | string | — | Classes for the <section>. |
- Photos are 200px tall (338px from 768px, 500px from 1024px). Figures stack below 768px and sit in three columns from 768px.
- Only the button moves (flip-button).
AI prompt
Paste into v0, Lovable, Bolt or Claude to rebuild this block in your own project.
Create a React + Tailwind CSS block called HeroSplit (Next.js). - <section className="py-(--section-y)">, container mx-auto max-w-(--container) px-(--gutter), column gap-12. - Title: stacked; from 768px a row, items-end, gap-12 (lg:gap-24): at the left (max 500px) a small muted label over an h1 (font-heading text-h1 semibold tracking-tight); at the right (max 400px) a text-lead muted lead and a button whose label rolls on hover. - Photos: a row h-50 / md:h-84.5 / lg:h-125, gap-2.5: the first 30% wide, the second the rest, rounded-xl object-cover. - Figures: stacked with h-px border-colored lines between; from 768px three columns with vertical lines. Each: font-heading text-h1 value, a font-medium title and a text-sm muted text. - Only shadcn variables and block tokens; plain <img> with alt text.











