Team
Team Grid
Small section header above portraits with name, role and email, in up to three columns.
Layout and motion from the Nitip template — see it in contextimport { cn } from "@/lib/utils";
/*
* Team Grid — a small section header (title, divider, lead at the right) above portraits with name, role and email.
* Source: marcframe/nitip → components/sections/TeamSection.tsx (members; the open positions are careers-accordion)
* + design-system TeamCard.
*
* Layout kept from Framer: one column below 768px, two from 768px, three from 1024px (gap 32 × 48). Portraits are
* 33:40. The role sits over the email on phones and beside it (email at the right) from 768px. The lead reaches up to
* 48rem at the right edge. No motion besides the email link's underline, as on Framer.
*/
export type TeamMember = { name: string; role: string; email?: string; photo: { src: string; alt: string } };
export type TeamGridProps = {
title?: string;
lead?: string;
members?: TeamMember[];
className?: string;
};
const portrait = (id: string) => `https://images.unsplash.com/${id}?auto=format&fit=crop&crop=faces&w=800&h=970&q=80`;
const defaults = {
title: "Meet the team",
lead: "Strategists, writers, designers and analysts who care about the numbers as much as the ideas.",
members: [
{ name: "Ava Morgan", role: "Account manager", email: "ava@northstar.example", photo: { src: portrait("photo-1438761681033-6461ffad8d80"), alt: "Ava Morgan" } },
{ name: "Daniel Brooks", role: "Creative director", email: "daniel@northstar.example", photo: { src: portrait("photo-1506794778202-cad84cf45f1d"), alt: "Daniel Brooks" } },
{ name: "Sofia Alvarez", role: "Content strategist", email: "sofia@northstar.example", photo: { src: portrait("photo-1517841905240-472988babdf9"), alt: "Sofia Alvarez" } },
{ name: "Leo Hart", role: "Paid media lead", email: "leo@northstar.example", photo: { src: portrait("photo-1522075469751-3a6694fb2f61"), alt: "Leo Hart" } },
{ name: "Nora Lane", role: "SEO specialist", email: "nora@northstar.example", photo: { src: portrait("photo-1531746020798-e6953c6e8e04"), alt: "Nora Lane" } },
{ name: "Sam Porter", role: "Data analyst", email: "sam@northstar.example", photo: { src: portrait("photo-1552058544-f2b08422138a"), alt: "Sam Porter" } },
],
};
export function TeamGrid({ title = defaults.title, lead = defaults.lead, members = defaults.members, className }: TeamGridProps) {
return (
<section className={cn("py-(--section-y)", className)}>
<div className="mx-auto flex max-w-(--container) flex-col gap-12 px-(--gutter) md:gap-16 lg:gap-20">
<div className="flex flex-col gap-5">
<h2 className="font-heading text-h3 font-semibold tracking-tight">{title}</h2>
<hr className="border-border" />
{lead && <p className="w-full max-w-3xl self-end text-h3 font-medium text-pretty">{lead}</p>}
</div>
<div className="grid gap-y-12 md:grid-cols-2 md:gap-x-8 lg:grid-cols-3">
{members.map((member) => (
<article key={member.name} className="flex flex-col gap-5">
<img
src={member.photo.src}
alt={member.photo.alt}
className="aspect-[33/40] w-full rounded-lg bg-muted object-cover"
/>
<div className="flex flex-col gap-1 md:gap-2">
<h3 className="font-heading text-h3 font-semibold tracking-tight">{member.name}</h3>
<div className="flex flex-col items-start gap-1 text-muted-foreground md:flex-row md:items-center md:gap-3">
<p className="md:flex-1">{member.role}</p>
{member.email && (
<a
href={`mailto:${member.email}`}
className="text-foreground transition-colors hover:text-foreground/80 hover:underline hover:underline-offset-4"
>
{member.email}
</a>
)}
</div>
</div>
</article>
))}
</div>
</div>
</section>
);
}Installation
$ npx shadcn@latest add https://framebrick.co/r/blocks/team-grid.json1. Add the block tokens to
app/globals.cssonce (theming).npx shadcn@latest add https://framebrick.co/r/blocks/tokens.json2. Copy the block into
components/team-grid.tsx.import { cn } from "@/lib/utils"; /* * Team Grid — a small section header (title, divider, lead at the right) above portraits with name, role and email. * Source: marcframe/nitip → components/sections/TeamSection.tsx (members; the open positions are careers-accordion) * + design-system TeamCard. * * Layout kept from Framer: one column below 768px, two from 768px, three from 1024px (gap 32 × 48). Portraits are * 33:40. The role sits over the email on phones and beside it (email at the right) from 768px. The lead reaches up to * 48rem at the right edge. No motion besides the email link's underline, as on Framer. */ export type TeamMember = { name: string; role: string; email?: string; photo: { src: string; alt: string } }; export type TeamGridProps = { title?: string; lead?: string; members?: TeamMember[]; className?: string; }; const portrait = (id: string) => `https://images.unsplash.com/${id}?auto=format&fit=crop&crop=faces&w=800&h=970&q=80`; const defaults = { title: "Meet the team", lead: "Strategists, writers, designers and analysts who care about the numbers as much as the ideas.", members: [ { name: "Ava Morgan", role: "Account manager", email: "ava@northstar.example", photo: { src: portrait("photo-1438761681033-6461ffad8d80"), alt: "Ava Morgan" } }, { name: "Daniel Brooks", role: "Creative director", email: "daniel@northstar.example", photo: { src: portrait("photo-1506794778202-cad84cf45f1d"), alt: "Daniel Brooks" } }, { name: "Sofia Alvarez", role: "Content strategist", email: "sofia@northstar.example", photo: { src: portrait("photo-1517841905240-472988babdf9"), alt: "Sofia Alvarez" } }, { name: "Leo Hart", role: "Paid media lead", email: "leo@northstar.example", photo: { src: portrait("photo-1522075469751-3a6694fb2f61"), alt: "Leo Hart" } }, { name: "Nora Lane", role: "SEO specialist", email: "nora@northstar.example", photo: { src: portrait("photo-1531746020798-e6953c6e8e04"), alt: "Nora Lane" } }, { name: "Sam Porter", role: "Data analyst", email: "sam@northstar.example", photo: { src: portrait("photo-1552058544-f2b08422138a"), alt: "Sam Porter" } }, ], }; export function TeamGrid({ title = defaults.title, lead = defaults.lead, members = defaults.members, className }: TeamGridProps) { return ( <section className={cn("py-(--section-y)", className)}> <div className="mx-auto flex max-w-(--container) flex-col gap-12 px-(--gutter) md:gap-16 lg:gap-20"> <div className="flex flex-col gap-5"> <h2 className="font-heading text-h3 font-semibold tracking-tight">{title}</h2> <hr className="border-border" /> {lead && <p className="w-full max-w-3xl self-end text-h3 font-medium text-pretty">{lead}</p>} </div> <div className="grid gap-y-12 md:grid-cols-2 md:gap-x-8 lg:grid-cols-3"> {members.map((member) => ( <article key={member.name} className="flex flex-col gap-5"> <img src={member.photo.src} alt={member.photo.alt} className="aspect-[33/40] w-full rounded-lg bg-muted object-cover" /> <div className="flex flex-col gap-1 md:gap-2"> <h3 className="font-heading text-h3 font-semibold tracking-tight">{member.name}</h3> <div className="flex flex-col items-start gap-1 text-muted-foreground md:flex-row md:items-center md:gap-3"> <p className="md:flex-1">{member.role}</p> {member.email && ( <a href={`mailto:${member.email}`} className="text-foreground transition-colors hover:text-foreground/80 hover:underline hover:underline-offset-4" > {member.email} </a> )} </div> </div> </article> ))} </div> </div> </section> ); }
Usage
import { TeamGrid } from "@/components/team-grid";
<TeamGrid
title="Meet the team"
members={[
{ name: "Ava Morgan", role: "Account manager", email: "ava@example.com", photo: { src: "/team/ava.jpg", alt: "Ava Morgan" } },
]}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | Meet the team | Section heading (h2), set small as on Framer. |
| lead | string | — | Large text at the right under the divider. |
| members | { name, role, email?, photo: { src, alt } }[] | 6 people | Cards; names are h3, emails are mailto links. |
| className | string | — | Classes for the <section>. |
- One column below 768px, two from 768px, three from 1024px. Portraits are 33:40.
- The role sits over the email on phones and beside it from 768px. No motion besides the email link's underline.
AI prompt
Paste into v0, Lovable, Bolt or Claude to rebuild this block in your own project.
Create a React + Tailwind CSS block called TeamGrid. - <section className="py-(--section-y)">, container mx-auto max-w-(--container) px-(--gutter), column gap-12 / md:gap-16 / lg:gap-20. - Header: a small h2 (font-heading text-h3 semibold), an <hr>, a lead (text-h3 font-medium) up to max-w-3xl aligned right. - Grid: 1 / 2 (768px) / 3 (1024px) columns, gap-x-8 gap-y-12. Card: a 33:40 portrait (rounded-lg, object-cover), an h3 name (font-heading text-h3 semibold), then the role (muted) and a mailto email link (foreground, underline on hover) — stacked on phones, a row with the email at the right from 768px. - Only shadcn variables and block tokens; plain <img> with the person's name as alt.











