Process
Process Overlay
Numbered steps joined by a line, in a card over a full-width photo.
Layout and motion from the Findit template — see it in contextimport { FlipButton } from "@/components/ui/flip-button";
import { cn } from "@/lib/utils";
/*
* Process Overlay — numbered steps joined by a line, in a card that sits over a full-width photo.
* Source: marcframe/findit → components/sections/ProcessSection.tsx (+ design-system ProcessStep).
*
* Layout kept from Framer: the photo covers the whole section; the card (full width below 768px, up to 500px from 768px,
* at the left of the container) holds the heading and lead, the steps and an action, 30px apart. A step: its number in
* a 38px column with a 2px line down to the next step, the title and text beside it; the last step has no line.
* The button rolls its label (flip-button, spring bounce 0, 0.4s); nothing else moves. The photo is decorative; text
* sits on the card, never on the photo. Dropped: Findit's dotted, uppercase label.
*/
export type OverlayStep = { title: string; text: string };
export type ProcessOverlayProps = {
/** Small line above the heading. */
label?: string;
title?: string;
lead?: string;
steps?: OverlayStep[];
action?: { label: string; href: string };
/** Photo behind the card (decorative). */
image?: string;
className?: string;
};
const defaults = {
label: "Our process",
title: "Three steps to your new home",
lead: "Buying, renting or investing — we keep it simple, from the first call to the day you move in.",
steps: [
{ title: "Discover", text: "Tell us what you need and we'll shortlist homes that fit, with honest notes on each." },
{ title: "Experience", text: "Visit in person or by video, at a time that suits you, with an agent who knows the area." },
{ title: "Secure", text: "We negotiate, handle the paperwork and stay with you until the keys are yours." },
],
action: { label: "Start your search", href: "/properties" },
image: "https://images.unsplash.com/photo-1512917774080-9991f1c4c750?auto=format&fit=crop&w=2000&q=80",
};
export function ProcessOverlay({
label = defaults.label,
title = defaults.title,
lead = defaults.lead,
steps = defaults.steps,
action = defaults.action,
image = defaults.image,
className,
}: ProcessOverlayProps) {
return (
<section className={cn("relative overflow-hidden py-(--section-y)", className)}>
<img src={image} alt="" className="absolute inset-0 size-full bg-muted object-cover" />
<div className="relative mx-auto max-w-(--container) px-(--gutter)">
<div className="flex w-full flex-col items-start gap-7.5 rounded-xl bg-background px-5 py-7.5 text-foreground md:max-w-125 md:p-7.5">
<div className="flex flex-col gap-5">
<div className="flex flex-col gap-2.5">
{label && <p className="text-sm font-medium text-muted-foreground">{label}</p>}
<h2 className="font-heading text-h2 font-semibold tracking-tight text-balance">{title}</h2>
</div>
{lead && <p className="text-pretty text-muted-foreground">{lead}</p>}
</div>
<ol className="flex w-full flex-col gap-2.5">
{steps.map((step, i) => (
<li key={step.title} className="flex gap-5">
<span className="flex w-9.5 shrink-0 flex-col items-start">
<span className="font-heading text-h3 font-semibold tracking-tight tabular-nums">
{String(i + 1).padStart(2, "0")}.
</span>
{i < steps.length - 1 && <span aria-hidden className="ml-3 w-0.5 flex-1 bg-foreground/10" />}
</span>
<div className="flex flex-1 flex-col gap-2.5 pb-5">
<h3 className="font-heading text-lg font-semibold tracking-tight">{step.title}</h3>
<p className="text-muted-foreground">{step.text}</p>
</div>
</li>
))}
</ol>
{action && <FlipButton href={action.href}>{action.label}</FlipButton>}
</div>
</div>
</section>
);
}Installation
$ npx shadcn@latest add https://framebrick.co/r/blocks/process-overlay.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/process-overlay.tsx.import { FlipButton } from "@/components/ui/flip-button"; import { cn } from "@/lib/utils"; /* * Process Overlay — numbered steps joined by a line, in a card that sits over a full-width photo. * Source: marcframe/findit → components/sections/ProcessSection.tsx (+ design-system ProcessStep). * * Layout kept from Framer: the photo covers the whole section; the card (full width below 768px, up to 500px from 768px, * at the left of the container) holds the heading and lead, the steps and an action, 30px apart. A step: its number in * a 38px column with a 2px line down to the next step, the title and text beside it; the last step has no line. * The button rolls its label (flip-button, spring bounce 0, 0.4s); nothing else moves. The photo is decorative; text * sits on the card, never on the photo. Dropped: Findit's dotted, uppercase label. */ export type OverlayStep = { title: string; text: string }; export type ProcessOverlayProps = { /** Small line above the heading. */ label?: string; title?: string; lead?: string; steps?: OverlayStep[]; action?: { label: string; href: string }; /** Photo behind the card (decorative). */ image?: string; className?: string; }; const defaults = { label: "Our process", title: "Three steps to your new home", lead: "Buying, renting or investing — we keep it simple, from the first call to the day you move in.", steps: [ { title: "Discover", text: "Tell us what you need and we'll shortlist homes that fit, with honest notes on each." }, { title: "Experience", text: "Visit in person or by video, at a time that suits you, with an agent who knows the area." }, { title: "Secure", text: "We negotiate, handle the paperwork and stay with you until the keys are yours." }, ], action: { label: "Start your search", href: "/properties" }, image: "https://images.unsplash.com/photo-1512917774080-9991f1c4c750?auto=format&fit=crop&w=2000&q=80", }; export function ProcessOverlay({ label = defaults.label, title = defaults.title, lead = defaults.lead, steps = defaults.steps, action = defaults.action, image = defaults.image, className, }: ProcessOverlayProps) { return ( <section className={cn("relative overflow-hidden py-(--section-y)", className)}> <img src={image} alt="" className="absolute inset-0 size-full bg-muted object-cover" /> <div className="relative mx-auto max-w-(--container) px-(--gutter)"> <div className="flex w-full flex-col items-start gap-7.5 rounded-xl bg-background px-5 py-7.5 text-foreground md:max-w-125 md:p-7.5"> <div className="flex flex-col gap-5"> <div className="flex flex-col gap-2.5"> {label && <p className="text-sm font-medium text-muted-foreground">{label}</p>} <h2 className="font-heading text-h2 font-semibold tracking-tight text-balance">{title}</h2> </div> {lead && <p className="text-pretty text-muted-foreground">{lead}</p>} </div> <ol className="flex w-full flex-col gap-2.5"> {steps.map((step, i) => ( <li key={step.title} className="flex gap-5"> <span className="flex w-9.5 shrink-0 flex-col items-start"> <span className="font-heading text-h3 font-semibold tracking-tight tabular-nums"> {String(i + 1).padStart(2, "0")}. </span> {i < steps.length - 1 && <span aria-hidden className="ml-3 w-0.5 flex-1 bg-foreground/10" />} </span> <div className="flex flex-1 flex-col gap-2.5 pb-5"> <h3 className="font-heading text-lg font-semibold tracking-tight">{step.title}</h3> <p className="text-muted-foreground">{step.text}</p> </div> </li> ))} </ol> {action && <FlipButton href={action.href}>{action.label}</FlipButton>} </div> </div> </section> ); }
Usage
import { ProcessOverlay } from "@/components/process-overlay";
<ProcessOverlay
image="/process/house.jpg"
steps={[
{ title: "Discover", text: "…" },
{ title: "Experience", text: "…" },
{ title: "Secure", text: "…" },
]}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | Our process | Small muted line above the heading. |
| title / lead | string | — | Heading (h2) and lead, in the card. |
| steps | { title, text }[] | 3 steps | Numbered, joined by a line; an ordered list with h3 titles. |
| action | { label, href } | Start your search | Rolling-label button at the bottom of the card. |
| image | string | — | Photo behind the card, covering the section (decorative). |
| className | string | — | Classes for the <section>. |
- The card is full width below 768px and up to 500px from 768px, at the left of the container. Text never sits on the photo.
- 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 ProcessOverlay (Next.js). - <section className="relative overflow-hidden py-(--section-y)"> with a cover photo (absolute inset-0, object-cover, alt ""). - In the container (relative, mx-auto max-w-(--container) px-(--gutter)) a card (rounded-xl bg-background px-5 py-7.5, md:p-7.5, full width, max 500px from 768px), gap-7.5: small muted label, h2 (font-heading text-h2 semibold), muted lead; an <ol> of steps — the number "01." (font-heading text-h3) in a 38px column with a 2px foreground/10 line running down to the next step (none after the last), an h3 title (text-lg semibold) and muted text with pb-5; a button whose label rolls on hover. - Only shadcn variables and block tokens.











