Text-only blog grid of six posts in three columns under a ruled top edge. Each post shows date, category, linked title, excerpt and a bold author name.
import { Heading } from "@/components/editorial/heading"
import {
Post,
PostAuthor,
PostCategory,
PostContent,
PostDate,
PostExcerpt,
PostGroup,
PostMeta,
PostTitle,
} from "@/components/editorial/post"
import {
Section,
SectionContent,
SectionDescription,
SectionHeader,
SectionTitleGroup,
} from "@/components/editorial/section"
// The copy: change text here, not in the markup.
const COPY = {
title: "Notes from the desk",
body: "Short pieces from the people who answer the messages, every other week.",
} as const
const POSTS = [
{
category: "Support",
title: "Say the answer first",
description:
"The customer asked one question. Answer it in the first line, then explain only what they still need.",
datetime: "2026-09-25",
date: "Sep 25, 2026",
author: "Mae Jemison",
href: "#",
},
{
category: "Product",
title: "Tags that mean something",
description:
"A tag earns its place when someone filters by it. We cut ours from forty to nine and reports got clearer.",
datetime: "2026-09-11",
date: "Sep 11, 2026",
author: "Ellen Ochoa",
href: "#",
},
{
category: "Team",
title: "The handoff note",
description:
"Three lines on what happened, what was tried and what is next. Whoever picks it up starts from there.",
datetime: "2026-08-28",
date: "Aug 28, 2026",
author: "Chris Hadfield",
href: "#",
},
{
category: "Customers",
title: "Thank the angry ones",
description:
"An angry message is a customer who still cares. The reply decides whether they stay that way.",
datetime: "2026-08-14",
date: "Aug 14, 2026",
author: "Sally Ride",
href: "#",
},
{
category: "Operations",
title: "Measure the slow week",
description:
"Averages hide the bad days. Track your slowest week each month and fix what caused it first.",
datetime: "2026-07-30",
date: "Jul 30, 2026",
author: "Peggy Whitson",
href: "#",
},
{
category: "Support",
title: "Saved replies, rewritten",
description:
"A saved reply is a draft, not a script. Ours now end with a line the agent writes for this one customer.",
datetime: "2026-07-16",
date: "Jul 16, 2026",
author: "Tim Peake",
href: "#",
},
] as const
export default function Blog04() {
return (
<Section>
<SectionHeader>
<SectionTitleGroup>
<Heading>{COPY.title}</Heading>
</SectionTitleGroup>
<SectionDescription>{COPY.body}</SectionDescription>
</SectionHeader>
<SectionContent>
<PostGroup cols={3} className="border-t border-foreground/10 pt-16">
{POSTS.map((post) => (
<Post key={post.title}>
<PostContent>
<PostMeta>
<PostDate dateTime={post.datetime}>{post.date}</PostDate>
<PostCategory>{post.category}</PostCategory>
</PostMeta>
<PostTitle>
<a href={post.href}>{post.title}</a>
</PostTitle>
<PostExcerpt>{post.description}</PostExcerpt>
<PostMeta className="mt-3">
<PostAuthor className="font-semibold text-foreground">
{post.author}
</PostAuthor>
</PostMeta>
</PostContent>
</Post>
))}
</PostGroup>
</SectionContent>
</Section>
)
}
pnpm dlx shadcn add @01orbital/blog-04npx shadcn add @01orbital/blog-04yarn shadcn add @01orbital/blog-04bunx --bun shadcn add @01orbital/blog-04Free under the MIT license. Add the @01orbital registry to components.json once (install guide), then run the command.
It lands at components/blocks/blog/blog-04/index.tsx, as source you own and edit.
import Blog04 from "@/components/blocks/blog/blog-04"
export default function Example() {
return <Blog04 />
}Registry pieces it installs with it:
Customer Stories Blog Grid
Customer stories in three columns, each headed by a square Rice Paper Plate tile in pine, mulberry or straw with a Phosphor icon, followed by year, company, title and summary.
Three-Card Blog Grid
Centred blog section with a heading and intro over a three-column grid of posts. Each card shows cover art, date and author, a linked title and a one-sentence excerpt.