Process
Process Autoplay
Steps that take turns every few seconds beside a photo that cross-fades to the active one.
Layout and motion from the Findit template — see it in context"use client";
import { FlipButton } from "@/components/ui/flip-button";
import { StepSequence } from "@/components/ui/step-sequence";
import { cn } from "@/lib/utils";
/*
* Process Autoplay — a heading, then steps that take turns every few seconds beside a photo that cross-fades to the
* active step, and an action.
* Source: marcframe/findit → components/sections/AboutProcessSection.tsx (+ design-system ProcessStep).
*
* Layout kept from Framer: below 1024px the steps sit above the photo (256px tall, 320px from 640px); from 1024px the
* steps take 40% of the row and the photo fills the rest at their height (at least 512px here — Framer's 644px text
* column included the heading).
* Motion kept (step-sequence): one step is active and the others sit at 70% (Framer: 50%); every 5s the next one takes over (after
* the last, the first); choosing a step makes it active at once; opacity and the photo cross-fade use a spring
* (bounce 0, 0.4s). step-sequence's improvements over Framer apply: a real tab list with arrow keys, the rotation
* pauses while hovered or focused, and it doesn't rotate with reduced motion.
* Changed: the heading spans the row above the steps and the photo, and the button follows them — Framer keeps both in
* the steps' column. The button rolls its label (flip-button). Dropped: Findit's dotted, uppercase label.
*/
export type AutoplayStep = { title: string; text: string; image: { src: string; alt: string } };
export type ProcessAutoplayProps = {
/** Small line above the heading. */
label?: string;
title?: string;
lead?: string;
steps?: AutoplayStep[];
/** Milliseconds each step stays active; 0 turns the rotation off. */
interval?: number;
action?: { label: string; href: string };
className?: string;
};
const photo = (id: string) => `https://images.unsplash.com/${id}?auto=format&fit=crop&w=1400&q=80`;
const defaults = {
label: "How we work",
title: "From first call to moving day",
lead: "Three steps, one team, no surprises along the way.",
steps: [
{
title: "Discover",
text: "We learn what you need and shortlist homes that fit your budget and your life.",
image: { src: photo("photo-1502672260266-1c1ef2d93688"), alt: "Living room with plants and a grey sofa" },
},
{
title: "Experience",
text: "Visit in person or by video with an agent who knows the building and the street.",
image: { src: photo("photo-1560448204-e02f11c3d0e2"), alt: "Bright living room with tall windows" },
},
{
title: "Secure",
text: "We negotiate, handle the paperwork and stay with you until the keys are yours.",
image: { src: photo("photo-1493809842364-78817add7ffb"), alt: "Living room with a blue sofa and white shelves" },
},
] as AutoplayStep[],
action: { label: "Start your journey", href: "/properties" },
};
export function ProcessAutoplay({
label = defaults.label,
title = defaults.title,
lead = defaults.lead,
steps = defaults.steps,
interval = 5000,
action = defaults.action,
className,
}: ProcessAutoplayProps) {
return (
<section className={cn("py-(--section-y)", className)}>
<div className="mx-auto flex max-w-(--container) flex-col items-start gap-12 px-(--gutter)">
<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="max-w-xl text-pretty text-muted-foreground">{lead}</p>}
</div>
<StepSequence
label={title}
interval={interval}
className="lg:min-h-128"
steps={steps.map((step) => ({
value: step.title,
title: step.title,
description: step.text,
media: <img src={step.image.src} alt={step.image.alt} />,
}))}
/>
{action && <FlipButton href={action.href}>{action.label}</FlipButton>}
</div>
</section>
);
}Installation
$ npx shadcn@latest add https://framebrick.co/r/blocks/process-autoplay.json1. Add the Framebrick UI components it uses (they bring Motion).
npx shadcn@latest add https://framebrick.co/r/step-sequence.json 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-autoplay.tsx."use client"; import { FlipButton } from "@/components/ui/flip-button"; import { StepSequence } from "@/components/ui/step-sequence"; import { cn } from "@/lib/utils"; /* * Process Autoplay — a heading, then steps that take turns every few seconds beside a photo that cross-fades to the * active step, and an action. * Source: marcframe/findit → components/sections/AboutProcessSection.tsx (+ design-system ProcessStep). * * Layout kept from Framer: below 1024px the steps sit above the photo (256px tall, 320px from 640px); from 1024px the * steps take 40% of the row and the photo fills the rest at their height (at least 512px here — Framer's 644px text * column included the heading). * Motion kept (step-sequence): one step is active and the others sit at 70% (Framer: 50%); every 5s the next one takes over (after * the last, the first); choosing a step makes it active at once; opacity and the photo cross-fade use a spring * (bounce 0, 0.4s). step-sequence's improvements over Framer apply: a real tab list with arrow keys, the rotation * pauses while hovered or focused, and it doesn't rotate with reduced motion. * Changed: the heading spans the row above the steps and the photo, and the button follows them — Framer keeps both in * the steps' column. The button rolls its label (flip-button). Dropped: Findit's dotted, uppercase label. */ export type AutoplayStep = { title: string; text: string; image: { src: string; alt: string } }; export type ProcessAutoplayProps = { /** Small line above the heading. */ label?: string; title?: string; lead?: string; steps?: AutoplayStep[]; /** Milliseconds each step stays active; 0 turns the rotation off. */ interval?: number; action?: { label: string; href: string }; className?: string; }; const photo = (id: string) => `https://images.unsplash.com/${id}?auto=format&fit=crop&w=1400&q=80`; const defaults = { label: "How we work", title: "From first call to moving day", lead: "Three steps, one team, no surprises along the way.", steps: [ { title: "Discover", text: "We learn what you need and shortlist homes that fit your budget and your life.", image: { src: photo("photo-1502672260266-1c1ef2d93688"), alt: "Living room with plants and a grey sofa" }, }, { title: "Experience", text: "Visit in person or by video with an agent who knows the building and the street.", image: { src: photo("photo-1560448204-e02f11c3d0e2"), alt: "Bright living room with tall windows" }, }, { title: "Secure", text: "We negotiate, handle the paperwork and stay with you until the keys are yours.", image: { src: photo("photo-1493809842364-78817add7ffb"), alt: "Living room with a blue sofa and white shelves" }, }, ] as AutoplayStep[], action: { label: "Start your journey", href: "/properties" }, }; export function ProcessAutoplay({ label = defaults.label, title = defaults.title, lead = defaults.lead, steps = defaults.steps, interval = 5000, action = defaults.action, className, }: ProcessAutoplayProps) { return ( <section className={cn("py-(--section-y)", className)}> <div className="mx-auto flex max-w-(--container) flex-col items-start gap-12 px-(--gutter)"> <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="max-w-xl text-pretty text-muted-foreground">{lead}</p>} </div> <StepSequence label={title} interval={interval} className="lg:min-h-128" steps={steps.map((step) => ({ value: step.title, title: step.title, description: step.text, media: <img src={step.image.src} alt={step.image.alt} />, }))} /> {action && <FlipButton href={action.href}>{action.label}</FlipButton>} </div> </section> ); }
Usage
import { ProcessAutoplay } from "@/components/process-autoplay";
<ProcessAutoplay
steps={[
{ title: "Discover", text: "…", image: { src: "/process/1.jpg", alt: "…" } },
{ title: "Experience", text: "…", image: { src: "/process/2.jpg", alt: "…" } },
]}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | How we work | Small muted line above the heading. |
| title / lead | string | — | Heading (h2) and lead above the steps; the title also names the step list. |
| steps | { title, text, image: { src, alt } }[] | 3 steps | Tabs with a photo each. |
| interval | number | 5000 | Milliseconds each step stays active; 0 turns the rotation off. |
| action | { label, href } | Start your journey | Rolling-label button under the steps. |
| className | string | — | Classes for the <section>. |
- Every 5s the next step becomes active and the photo cross-fades (step-sequence, spring bounce 0, 0.4s); the others sit at 70% (Framer: 50%, raised for contrast).
- The steps are a tab list (arrow keys, Home, End); the rotation pauses while hovered or focused and never runs with reduced motion.
- From 1024px the steps take 40% and the photo the rest, at least 512px tall; below, the photo follows the steps.
AI prompt
Paste into v0, Lovable, Bolt or Claude to rebuild this block in your own project.
Create a React + Tailwind CSS + Motion block called ProcessAutoplay (Next.js). - <section className="py-(--section-y)">, container mx-auto max-w-(--container) px-(--gutter), column items-start gap-12: a small muted label, an h2 (font-heading text-h2 semibold) and a muted lead; then the steps; then a button whose label rolls on hover. - Steps: a vertical tab list (role tablist, aria-selected, arrow keys / Home / End) — each tab the number "01.", a 2px line to the next, the title and a muted text; the active one at opacity 1, the rest 0.7 (spring bounce 0, 0.4s). Beside it from 1024px (40% / the rest, row at least 512px tall; below 1024px under it, 256px / 320px tall) a tabpanel whose photos cross-fade to the active step's. - Advance to the next step every 5 seconds (wrapping); clicking a step activates it and restarts the timer; pause while hovered or focused; no rotation with prefers-reduced-motion. - Only shadcn variables and block tokens; plain <img> with alt text.











