FAQ
FAQ Split
A heading and lead, then a portrait photo beside questions that open on their own.
Layout and motion from the Findit template — see it in contextimport { AccordionList } from "@/components/ui/accordion-list";
import { cn } from "@/lib/utils";
/*
* FAQ Split — a heading with a short lead, then a portrait photo beside the questions.
* Source: marcframe/findit → components/sections/FaqSection.tsx (+ design-system SectionTitle, AccordionItem).
*
* Layout kept from Framer: the title and lead stack at the left (lead up to 32rem). Below 768px the photo sits above
* the questions; from 768px it takes 35% of the row at a 0.726 ratio beside them, and from 1024px the questions get 32px
* of room above and below.
* Motion kept (accordion-list): the first question starts open, every row opens and closes on its own, height with a
* spring (bounce 0, 0.4s) and the answer fading in. Changed: the icon is accordion-list's plus that turns into a
* cross, where Findit swaps a minus and a plus. Dropped: Findit's dotted, uppercase label.
*/
export type FaqItem = { question: string; answer: string };
export type FaqSplitProps = {
/** Small line above the heading. */
label?: string;
title?: string;
lead?: string;
image?: { src: string; alt: string };
items?: FaqItem[];
className?: string;
};
const defaults = {
label: "FAQ",
title: "Frequently asked questions",
lead: "The questions we hear most from buyers, sellers and renters. Can't find yours? Our team is a message away.",
image: {
src: "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?auto=format&fit=crop&crop=faces&w=900&h=1240&q=80",
alt: "Smiling woman in a grey blazer beside an office window",
},
items: [
{
question: "How do I start buying a home with you?",
answer: "Book a free consultation. We'll talk through your budget, the areas you like and your timeline, then send a shortlist within a week.",
},
{
question: "Do you help with mortgages?",
answer: "We work with independent brokers who compare lenders for you, and we can introduce you at the first meeting.",
},
{
question: "How long does selling a property take?",
answer: "Most homes we list sell within six to ten weeks. Pricing it right and good photos make the biggest difference.",
},
{
question: "Can I rent a home before I buy?",
answer: "Yes. Many of our listings are available to rent, and renting in an area first is a good way to be sure.",
},
{
question: "What fees should I expect?",
answer: "Buyers pay no agency fee. Sellers pay a fixed commission agreed up front, with no costs if the home doesn't sell.",
},
{
question: "Do you arrange viewings on weekends?",
answer: "Every day of the week, in person or by video call, whichever suits you best.",
},
],
};
export function FaqSplit({
label = defaults.label,
title = defaults.title,
lead = defaults.lead,
image = defaults.image,
items = defaults.items,
className,
}: FaqSplitProps) {
return (
<section className={cn("py-(--section-y)", className)}>
<div className="mx-auto flex max-w-(--container) flex-col gap-14 px-(--gutter) md:gap-12">
<div className="flex flex-col items-start 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-lg text-pretty text-muted-foreground">{lead}</p>}
</div>
<div className="flex flex-col gap-12 md:flex-row md:items-start">
<img
src={image.src}
alt={image.alt}
className="aspect-[0.726] w-full rounded-xl bg-muted object-cover md:w-[35%] md:shrink-0"
/>
<AccordionList
type="multiple"
dim={false}
defaultValue={items.length ? ["q0"] : []}
className="min-w-0 flex-1 lg:py-8"
items={items.map((item, i) => ({
// The value becomes part of element ids, so it is the index, not the question text.
value: `q${i}`,
title: <span className="text-lead font-medium">{item.question}</span>,
content: <p className="text-base">{item.answer}</p>,
}))}
/>
</div>
</div>
</section>
);
}Installation
$ npx shadcn@latest add https://framebrick.co/r/blocks/faq-split.json1. Add the Framebrick UI components it uses (they bring Motion).
npx shadcn@latest add https://framebrick.co/r/accordion-list.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/faq-split.tsx.import { AccordionList } from "@/components/ui/accordion-list"; import { cn } from "@/lib/utils"; /* * FAQ Split — a heading with a short lead, then a portrait photo beside the questions. * Source: marcframe/findit → components/sections/FaqSection.tsx (+ design-system SectionTitle, AccordionItem). * * Layout kept from Framer: the title and lead stack at the left (lead up to 32rem). Below 768px the photo sits above * the questions; from 768px it takes 35% of the row at a 0.726 ratio beside them, and from 1024px the questions get 32px * of room above and below. * Motion kept (accordion-list): the first question starts open, every row opens and closes on its own, height with a * spring (bounce 0, 0.4s) and the answer fading in. Changed: the icon is accordion-list's plus that turns into a * cross, where Findit swaps a minus and a plus. Dropped: Findit's dotted, uppercase label. */ export type FaqItem = { question: string; answer: string }; export type FaqSplitProps = { /** Small line above the heading. */ label?: string; title?: string; lead?: string; image?: { src: string; alt: string }; items?: FaqItem[]; className?: string; }; const defaults = { label: "FAQ", title: "Frequently asked questions", lead: "The questions we hear most from buyers, sellers and renters. Can't find yours? Our team is a message away.", image: { src: "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?auto=format&fit=crop&crop=faces&w=900&h=1240&q=80", alt: "Smiling woman in a grey blazer beside an office window", }, items: [ { question: "How do I start buying a home with you?", answer: "Book a free consultation. We'll talk through your budget, the areas you like and your timeline, then send a shortlist within a week.", }, { question: "Do you help with mortgages?", answer: "We work with independent brokers who compare lenders for you, and we can introduce you at the first meeting.", }, { question: "How long does selling a property take?", answer: "Most homes we list sell within six to ten weeks. Pricing it right and good photos make the biggest difference.", }, { question: "Can I rent a home before I buy?", answer: "Yes. Many of our listings are available to rent, and renting in an area first is a good way to be sure.", }, { question: "What fees should I expect?", answer: "Buyers pay no agency fee. Sellers pay a fixed commission agreed up front, with no costs if the home doesn't sell.", }, { question: "Do you arrange viewings on weekends?", answer: "Every day of the week, in person or by video call, whichever suits you best.", }, ], }; export function FaqSplit({ label = defaults.label, title = defaults.title, lead = defaults.lead, image = defaults.image, items = defaults.items, className, }: FaqSplitProps) { return ( <section className={cn("py-(--section-y)", className)}> <div className="mx-auto flex max-w-(--container) flex-col gap-14 px-(--gutter) md:gap-12"> <div className="flex flex-col items-start 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-lg text-pretty text-muted-foreground">{lead}</p>} </div> <div className="flex flex-col gap-12 md:flex-row md:items-start"> <img src={image.src} alt={image.alt} className="aspect-[0.726] w-full rounded-xl bg-muted object-cover md:w-[35%] md:shrink-0" /> <AccordionList type="multiple" dim={false} defaultValue={items.length ? ["q0"] : []} className="min-w-0 flex-1 lg:py-8" items={items.map((item, i) => ({ // The value becomes part of element ids, so it is the index, not the question text. value: `q${i}`, title: <span className="text-lead font-medium">{item.question}</span>, content: <p className="text-base">{item.answer}</p>, }))} /> </div> </div> </section> ); }
Usage
import { FaqSplit } from "@/components/faq-split";
<FaqSplit
title="Frequently asked questions"
image={{ src: "/team/agent.jpg", alt: "Our lead agent" }}
items={[{ question: "How do I start?", answer: "Book a free consultation." }]}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | FAQ | Small muted line above the heading. |
| title / lead | string | — | Section heading (h2) and the text under it (up to 32rem). |
| image | { src, alt } | — | Portrait photo at a 0.726 ratio. |
| items | { question, answer }[] | 6 questions | Rows; questions are buttons inside h3. |
| className | string | — | Classes for the <section>. |
- The photo sits above the questions below 768px and takes 35% of the row beside them from 768px.
- The first question starts open; every row opens on its own (accordion-list, spring bounce 0, 0.4s). Rows don't dim — the questions stay readable.
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 FaqSplit. - <section className="py-(--section-y)">, container mx-auto max-w-(--container) px-(--gutter), column gap-14 (md:gap-12). - Title: a small muted label and an h2 (font-heading text-h2 semibold tracking-tight), then a muted lead up to max-w-lg. - Content: stacked below 768px (gap-12); from 768px a row, items-start: a portrait photo (aspect 0.726, rounded-xl, 35% wide) and the questions (flex-1, lg:py-8): divided rows, each a button inside an h3 with the question (text-lead font-medium) and a plus that turns 45° when open; aria-expanded / aria-controls; several can be open; the first starts open; height 0 → auto with a spring (bounce 0, 0.4s) and the answer fading in. - Only shadcn variables and block tokens; plain <img> with alt text.











