Stats & values
About Facts
A long statement, a photo beside two short paragraphs and an action, then four figures on cards.
Layout and motion from the Kalme template — see it in contextimport { FlipButton } from "@/components/ui/flip-button";
import { cn } from "@/lib/utils";
/*
* About Facts — who you are in one long statement, a photo beside two short paragraphs and an action, then four
* figures on cards.
* Source: marcframe/kalme → components/sections/HomeAboutSection.tsx (+ design-system FactCard, SectionLabel).
*
* Layout kept from Framer: the label and the heading span the width; below them the photo (3:2) and the text stack
* 48px apart, and from 1024px they share the row in equal halves, aligned to the bottom. The paragraphs sit side by
* side from 768px, the button under them. Cards: one column, two from 768px, four from 1024px, 10px apart; each holds
* the figure with a smaller muted suffix ("80" + "+") over its label.
* The button rolls its label (flip-button, spring bounce 0, 0.4s); nothing else moves. Dropped: Kalme's red dot in the
* label. The photo is decorative.
*/
export type AboutFact = { value: string; suffix?: string; label: string };
export type AboutFactsProps = {
/** Small line above the heading. */
label?: string;
title?: string;
/** Decorative photo beside the paragraphs (3:2). */
image?: string;
/** One or two short paragraphs. */
paragraphs?: string[];
action?: { label: string; href: string };
facts?: AboutFact[];
className?: string;
};
const defaults = {
label: "Who we are",
title: "A small team of strategists, writers and designers who turn attention into growth for ambitious brands.",
image: "https://images.unsplash.com/photo-1559136555-9303baea8ebd?auto=format&fit=crop&w=1200&q=80",
paragraphs: [
"We started as three freelancers sharing a desk. Today we plan, make and measure campaigns for brands on four continents.",
"Every project starts with a question — what should change? — and ends with numbers our clients can take to their board.",
],
action: { label: "More about us", href: "/about" },
facts: [
{ value: "80", suffix: "+", label: "Brands grown since 2018" },
{ value: "12", suffix: "M", label: "Monthly ad impressions managed" },
{ value: "3.4", suffix: "×", label: "Average return on ad spend" },
{ value: "96", suffix: "%", label: "Clients who stay a second year" },
] as AboutFact[],
};
export function AboutFacts({
label = defaults.label,
title = defaults.title,
image = defaults.image,
paragraphs = defaults.paragraphs,
action = defaults.action,
facts = defaults.facts,
className,
}: AboutFactsProps) {
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-2.5">
{label && <p className="text-sm font-medium text-muted-foreground">{label}</p>}
<h2 className="max-w-5xl font-heading text-h2 font-semibold tracking-tight text-balance">{title}</h2>
</div>
<div className="flex flex-col gap-12 lg:flex-row lg:items-end">
{image && <img src={image} alt="" className="aspect-[3/2] w-full rounded-xl bg-muted object-cover lg:w-auto lg:min-w-0 lg:flex-1" />}
<div className="flex flex-col items-start gap-7.5 lg:flex-1">
{paragraphs.length > 0 && (
<div className="flex flex-col gap-2.5 md:flex-row md:gap-12">
{paragraphs.map((paragraph) => (
<p key={paragraph} className="flex-1 text-pretty text-muted-foreground">
{paragraph}
</p>
))}
</div>
)}
{action && <FlipButton href={action.href}>{action.label}</FlipButton>}
</div>
</div>
{facts.length > 0 && (
<dl className="grid gap-2.5 md:grid-cols-2 lg:grid-cols-4">
{facts.map((fact) => (
/* The label comes first for screen readers (a <dl> puts terms before values); the figure shows on top. */
<div key={fact.label} className="flex flex-col-reverse justify-end gap-1 rounded-xl bg-muted p-7.5">
<dt className="text-foreground/65">{fact.label}</dt>
<dd className="flex items-baseline gap-0.5">
<span className="font-heading text-h1 font-semibold tracking-tight">{fact.value}</span>
{fact.suffix && <span className="font-heading text-h3 font-semibold text-foreground/65">{fact.suffix}</span>}
</dd>
</div>
))}
</dl>
)}
</div>
</section>
);
}Installation
$ npx shadcn@latest add https://framebrick.co/r/blocks/about-facts.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/about-facts.tsx.import { FlipButton } from "@/components/ui/flip-button"; import { cn } from "@/lib/utils"; /* * About Facts — who you are in one long statement, a photo beside two short paragraphs and an action, then four * figures on cards. * Source: marcframe/kalme → components/sections/HomeAboutSection.tsx (+ design-system FactCard, SectionLabel). * * Layout kept from Framer: the label and the heading span the width; below them the photo (3:2) and the text stack * 48px apart, and from 1024px they share the row in equal halves, aligned to the bottom. The paragraphs sit side by * side from 768px, the button under them. Cards: one column, two from 768px, four from 1024px, 10px apart; each holds * the figure with a smaller muted suffix ("80" + "+") over its label. * The button rolls its label (flip-button, spring bounce 0, 0.4s); nothing else moves. Dropped: Kalme's red dot in the * label. The photo is decorative. */ export type AboutFact = { value: string; suffix?: string; label: string }; export type AboutFactsProps = { /** Small line above the heading. */ label?: string; title?: string; /** Decorative photo beside the paragraphs (3:2). */ image?: string; /** One or two short paragraphs. */ paragraphs?: string[]; action?: { label: string; href: string }; facts?: AboutFact[]; className?: string; }; const defaults = { label: "Who we are", title: "A small team of strategists, writers and designers who turn attention into growth for ambitious brands.", image: "https://images.unsplash.com/photo-1559136555-9303baea8ebd?auto=format&fit=crop&w=1200&q=80", paragraphs: [ "We started as three freelancers sharing a desk. Today we plan, make and measure campaigns for brands on four continents.", "Every project starts with a question — what should change? — and ends with numbers our clients can take to their board.", ], action: { label: "More about us", href: "/about" }, facts: [ { value: "80", suffix: "+", label: "Brands grown since 2018" }, { value: "12", suffix: "M", label: "Monthly ad impressions managed" }, { value: "3.4", suffix: "×", label: "Average return on ad spend" }, { value: "96", suffix: "%", label: "Clients who stay a second year" }, ] as AboutFact[], }; export function AboutFacts({ label = defaults.label, title = defaults.title, image = defaults.image, paragraphs = defaults.paragraphs, action = defaults.action, facts = defaults.facts, className, }: AboutFactsProps) { 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-2.5"> {label && <p className="text-sm font-medium text-muted-foreground">{label}</p>} <h2 className="max-w-5xl font-heading text-h2 font-semibold tracking-tight text-balance">{title}</h2> </div> <div className="flex flex-col gap-12 lg:flex-row lg:items-end"> {image && <img src={image} alt="" className="aspect-[3/2] w-full rounded-xl bg-muted object-cover lg:w-auto lg:min-w-0 lg:flex-1" />} <div className="flex flex-col items-start gap-7.5 lg:flex-1"> {paragraphs.length > 0 && ( <div className="flex flex-col gap-2.5 md:flex-row md:gap-12"> {paragraphs.map((paragraph) => ( <p key={paragraph} className="flex-1 text-pretty text-muted-foreground"> {paragraph} </p> ))} </div> )} {action && <FlipButton href={action.href}>{action.label}</FlipButton>} </div> </div> {facts.length > 0 && ( <dl className="grid gap-2.5 md:grid-cols-2 lg:grid-cols-4"> {facts.map((fact) => ( /* The label comes first for screen readers (a <dl> puts terms before values); the figure shows on top. */ <div key={fact.label} className="flex flex-col-reverse justify-end gap-1 rounded-xl bg-muted p-7.5"> <dt className="text-foreground/65">{fact.label}</dt> <dd className="flex items-baseline gap-0.5"> <span className="font-heading text-h1 font-semibold tracking-tight">{fact.value}</span> {fact.suffix && <span className="font-heading text-h3 font-semibold text-foreground/65">{fact.suffix}</span>} </dd> </div> ))} </dl> )} </div> </section> ); }
Usage
import { AboutFacts } from "@/components/about-facts";
<AboutFacts
title="A small team of strategists, writers and designers…"
image="/about/team.jpg"
paragraphs={["We started as three freelancers…", "Every project starts with a question…"]}
facts={[
{ value: "80", suffix: "+", label: "Brands grown since 2018" },
{ value: "3.4", suffix: "×", label: "Average return on ad spend" },
]}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | Who we are | Small muted line above the heading. |
| title | string | — | A long statement (h2) across the width. |
| image | string | — | Decorative photo (3:2) beside the paragraphs. |
| paragraphs | string[] | — | One or two short paragraphs, side by side from 768px. |
| action | { label, href } | More about us | Button under the paragraphs. |
| facts | { value, suffix?, label }[] | 4 facts | Cards with the figure, a smaller muted suffix and the label. |
| className | string | — | Classes for the <section>. |
- The photo and the text stack below 1024px and share the row in equal halves from 1024px, aligned to the bottom.
- Cards: one column, two from 768px, four from 1024px. The figures are a description list (label, then value).
- The button rolls its label (flip-button, spring bounce 0, 0.4s); nothing else moves.
AI prompt
Paste into v0, Lovable, Bolt or Claude to rebuild this block in your own project.
Create a React + Tailwind CSS block called AboutFacts (Next.js). - <section className="py-(--section-y)">, container mx-auto max-w-(--container) px-(--gutter), column gap-12. - A small muted label over a long h2 (font-heading text-h2 semibold, max-w-5xl). - A row from 1024px (items-end, gap-12, equal halves; stacked below): a 3:2 photo (rounded-xl) and a column (gap-7.5): two muted paragraphs side by side from 768px (gap-12), then a button whose label rolls up on hover (spring bounce 0, 0.4s). - A <dl> grid (gap-2.5; 1 / 2 from 768px / 4 from 1024px) of cards (rounded-xl bg-muted p-7.5): the figure (font-heading text-h1) with a suffix (text-h3) in text-foreground/65 on top, the label (text-foreground/65) under it. - Only shadcn variables and block tokens (secondary text on bg-muted is text-foreground/65: muted-foreground falls under 4.5:1 there); plain <img>.











