Blog
Blog Article
The body of a post beside its date and category, styled for your Markdown or MDX.
Layout and motion from the Nitip template — see it in contextimport * as React from "react";
import { cn } from "@/lib/utils";
/*
* Blog Article — the body of a post beside its meta: date and category in a column at the left from 1024px, a row
* above the text below that.
* Source: marcframe/nitip → components/sections/PostContentSection.tsx (+ design-system RichText).
*
* Layout kept from Framer: from 1024px the meta is a 300px column (date, divider, category) and the body is 755px
* wide; below 1024px date and category share a row above the body, each half the width. In the body headings get
* 40px above them and paragraphs and lists 20px; lists indent 20px per level with the markers outside. No motion.
* The body is yours: pass rendered Markdown / MDX / HTML as children — headings in it are h2 (the post title above
* is the page's h1), styled from here. Without children the block shows a sample article.
*/
export type BlogArticleProps = {
/** Shown as is, e.g. "Jun 25, 2026" — format it where you have the locale. */
date?: string;
/** Machine-readable date for <time>, e.g. "2026-06-25". */
dateTime?: string;
category?: string;
labels?: { date: string; category: string };
children?: React.ReactNode;
className?: string;
};
const defaults = {
date: "Jun 25, 2026",
dateTime: "2026-06-25",
category: "Social media",
labels: { date: "Posted on", category: "Category" },
};
function SampleBody() {
return (
<>
<p>
Most small teams don't run out of ideas — they run out of time. The fix isn't more ideas; it's a
routine that turns a few good ones into a month of posts without anyone working late.
</p>
<h2>Start from three pillars</h2>
<p>
Pick three things your audience keeps asking about and your team can talk about with authority. Every post
should belong to one of them. It narrows the blank page and makes your feed easy to recognise.
</p>
<ul>
<li>
<strong>Teach:</strong> short how-tos drawn from real questions.
</li>
<li>
<strong>Show:</strong> the work, the people and the process behind it.
<ul>
<li>Behind-the-scenes clips beat polished shots for reach.</li>
<li>One person on camera builds trust faster than a logo.</li>
</ul>
</li>
<li>
<strong>Prove:</strong> results, reviews and before-and-afters.
</li>
</ul>
<h2>Batch it in one afternoon</h2>
<p>
Block three hours once a month. Write every caption first, then shoot or design in one go, then schedule
everything before you stop. Switching tasks less often is where the time comes back.
</p>
<ol>
<li>Draft twelve captions from your pillars.</li>
<li>Make the visuals in a single session.</li>
<li>Schedule the month and leave room for news.</li>
</ol>
<p>
Review the numbers at the end of the month, keep what worked, and let the rest go. The routine gets faster every
time.
</p>
</>
);
}
export function BlogArticle({
date = defaults.date,
dateTime = defaults.dateTime,
category = defaults.category,
labels = defaults.labels,
children,
className,
}: BlogArticleProps) {
return (
<section className={cn("py-(--section-y)", className)}>
<div className="mx-auto flex max-w-(--container) flex-col gap-12 px-(--gutter) lg:flex-row">
<dl className="grid grid-cols-2 gap-5 lg:flex lg:w-75 lg:shrink-0 lg:flex-col lg:self-start">
<div className="flex flex-col gap-2.5">
<dt className="text-sm text-muted-foreground">{labels.date}</dt>
<dd className="text-lead">
<time dateTime={dateTime}>{date}</time>
</dd>
</div>
<div className="flex flex-col gap-2.5 lg:border-t lg:border-border lg:pt-5">
<dt className="text-sm text-muted-foreground">{labels.category}</dt>
<dd className="text-lead">{category}</dd>
</div>
</dl>
<div className="min-w-0 flex-1 text-pretty lg:max-w-[47rem] [&_h2]:mt-10 [&_h2]:font-heading [&_h2]:text-h3 [&_h2]:font-semibold [&_h2]:tracking-tight [&_h2]:text-foreground [&_li]:mt-1 [&_ol]:mt-5 [&_ol]:list-decimal [&_ol]:pl-5 [&_ol_ol]:mt-1 [&_ol_ul]:mt-1 [&_p]:mt-5 [&_strong]:font-semibold [&_strong]:text-foreground [&_ul]:mt-5 [&_ul]:list-disc [&_ul]:pl-5 [&_ul_ol]:mt-1 [&_ul_ul]:mt-1 [&>:first-child]:mt-0 text-muted-foreground">
{children ?? <SampleBody />}
</div>
</div>
</section>
);
}Installation
$ npx shadcn@latest add https://framebrick.co/r/blocks/blog-article.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/blog-article.tsx.import * as React from "react"; import { cn } from "@/lib/utils"; /* * Blog Article — the body of a post beside its meta: date and category in a column at the left from 1024px, a row * above the text below that. * Source: marcframe/nitip → components/sections/PostContentSection.tsx (+ design-system RichText). * * Layout kept from Framer: from 1024px the meta is a 300px column (date, divider, category) and the body is 755px * wide; below 1024px date and category share a row above the body, each half the width. In the body headings get * 40px above them and paragraphs and lists 20px; lists indent 20px per level with the markers outside. No motion. * The body is yours: pass rendered Markdown / MDX / HTML as children — headings in it are h2 (the post title above * is the page's h1), styled from here. Without children the block shows a sample article. */ export type BlogArticleProps = { /** Shown as is, e.g. "Jun 25, 2026" — format it where you have the locale. */ date?: string; /** Machine-readable date for <time>, e.g. "2026-06-25". */ dateTime?: string; category?: string; labels?: { date: string; category: string }; children?: React.ReactNode; className?: string; }; const defaults = { date: "Jun 25, 2026", dateTime: "2026-06-25", category: "Social media", labels: { date: "Posted on", category: "Category" }, }; function SampleBody() { return ( <> <p> Most small teams don't run out of ideas — they run out of time. The fix isn't more ideas; it's a routine that turns a few good ones into a month of posts without anyone working late. </p> <h2>Start from three pillars</h2> <p> Pick three things your audience keeps asking about and your team can talk about with authority. Every post should belong to one of them. It narrows the blank page and makes your feed easy to recognise. </p> <ul> <li> <strong>Teach:</strong> short how-tos drawn from real questions. </li> <li> <strong>Show:</strong> the work, the people and the process behind it. <ul> <li>Behind-the-scenes clips beat polished shots for reach.</li> <li>One person on camera builds trust faster than a logo.</li> </ul> </li> <li> <strong>Prove:</strong> results, reviews and before-and-afters. </li> </ul> <h2>Batch it in one afternoon</h2> <p> Block three hours once a month. Write every caption first, then shoot or design in one go, then schedule everything before you stop. Switching tasks less often is where the time comes back. </p> <ol> <li>Draft twelve captions from your pillars.</li> <li>Make the visuals in a single session.</li> <li>Schedule the month and leave room for news.</li> </ol> <p> Review the numbers at the end of the month, keep what worked, and let the rest go. The routine gets faster every time. </p> </> ); } export function BlogArticle({ date = defaults.date, dateTime = defaults.dateTime, category = defaults.category, labels = defaults.labels, children, className, }: BlogArticleProps) { return ( <section className={cn("py-(--section-y)", className)}> <div className="mx-auto flex max-w-(--container) flex-col gap-12 px-(--gutter) lg:flex-row"> <dl className="grid grid-cols-2 gap-5 lg:flex lg:w-75 lg:shrink-0 lg:flex-col lg:self-start"> <div className="flex flex-col gap-2.5"> <dt className="text-sm text-muted-foreground">{labels.date}</dt> <dd className="text-lead"> <time dateTime={dateTime}>{date}</time> </dd> </div> <div className="flex flex-col gap-2.5 lg:border-t lg:border-border lg:pt-5"> <dt className="text-sm text-muted-foreground">{labels.category}</dt> <dd className="text-lead">{category}</dd> </div> </dl> <div className="min-w-0 flex-1 text-pretty lg:max-w-[47rem] [&_h2]:mt-10 [&_h2]:font-heading [&_h2]:text-h3 [&_h2]:font-semibold [&_h2]:tracking-tight [&_h2]:text-foreground [&_li]:mt-1 [&_ol]:mt-5 [&_ol]:list-decimal [&_ol]:pl-5 [&_ol_ol]:mt-1 [&_ol_ul]:mt-1 [&_p]:mt-5 [&_strong]:font-semibold [&_strong]:text-foreground [&_ul]:mt-5 [&_ul]:list-disc [&_ul]:pl-5 [&_ul_ol]:mt-1 [&_ul_ul]:mt-1 [&>:first-child]:mt-0 text-muted-foreground"> {children ?? <SampleBody />} </div> </div> </section> ); }
Usage
import { BlogArticle } from "@/components/blog-article";
<BlogArticle date="Jun 25, 2026" dateTime="2026-06-25" category="Social media">
<MDXContent />
</BlogArticle>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| date / dateTime | string | — | Date as shown, and its machine-readable form for <time>. |
| category | string | — | Shown under the date (beside it below 1024px). |
| labels | { date, category } | Posted on, Category | Labels of the two meta items. |
| children | ReactNode | — | The article: rendered Markdown / MDX / HTML. Without it, a sample article. |
| className | string | — | Classes for the <section>. |
- From 1024px the meta is a 300px column (date, line, category) and the body 47rem wide; below that date and category share a row above the body.
- The body styles its h2, p, ul, ol (nested lists indent 20px, markers outside) and strong: headings get 40px above, paragraphs and lists 20px. Headings in the body are h2 — the post title above is the page's h1.
- No motion.
AI prompt
Paste into v0, Lovable, Bolt or Claude to rebuild this block in your own project.
Create a React + Tailwind CSS block called BlogArticle. - <section className="py-(--section-y)">, container mx-auto max-w-(--container) px-(--gutter); a column that becomes a row from 1024px (gap-12). - Meta: a <dl> with "Posted on" (a <time>) and "Category" — labels text-sm muted, values text-lead; two columns side by side below 1024px; from 1024px a 300px column with a border-t between the two. - Body (children, max 47rem from 1024px, muted text): style descendants — h2 font-heading text-h3 semibold foreground with mt-10; p, ul, ol mt-5; lists list-disc / list-decimal pl-5, nested lists mt-1; strong semibold foreground; first child mt-0. - Only shadcn variables and block tokens. No motion.











