All Taste components
Blog
Blog — featured post with list
One large featured article on the left and a list of recent posts with small thumbnails on the right.
featured post · side list with thumbnails · reading time · stacks on mobile
import { ArrowRight } from "lucide-react";
type Post = { title: string; excerpt?: string; tag: string; href: string; cover: string; date: string; dateTime: string; minutes: number; author?: string };
const featured: Post = {
title: "How to write payment terms clients actually read", excerpt: "Five lines that cut late payments without sounding strict, with examples you can copy.",
tag: "Guides", href: "/blog/payment-terms", cover: "/blog/payment-terms.jpg", date: "Oct 8, 2026", dateTime: "2026-10-08", minutes: 6, author: "Ingrid Solberg",
};
const recent: Post[] = [
{ title: "Late fees: when to charge them and how much", tag: "Getting paid", href: "/blog/late-fees", cover: "/blog/late-fees.jpg", date: "Sep 29, 2026", dateTime: "2026-09-29", minutes: 8 },
{ title: "What’s new: recurring invoices for retainers", tag: "Product", href: "/blog/recurring-invoices", cover: "/blog/recurring.jpg", date: "Sep 17, 2026", dateTime: "2026-09-17", minutes: 3 },
{ title: "The month-end checklist for a one-person business", tag: "Guides", href: "/blog/month-end", cover: "/blog/month-end.jpg", date: "Sep 4, 2026", dateTime: "2026-09-04", minutes: 5 },
{ title: "Quotes vs estimates vs invoices, explained", tag: "Guides", href: "/blog/quotes-estimates", cover: "/blog/quotes.jpg", date: "Aug 21, 2026", dateTime: "2026-08-21", minutes: 4 },
];
export function BlogFeaturedList() {
return (
<section aria-labelledby="journal-title" className="bg-bg px-5 py-16 font-sans sm:px-10 sm:py-20">
<div className="mx-auto max-w-6xl">
<h2 id="journal-title" className="text-3xl font-semibold tracking-tight text-fg sm:text-4xl">The Tallybook journal</h2>
<div className="mt-10 grid gap-9 lg:grid-cols-[1.25fr_1fr] lg:gap-12">
<a href={featured.href} className="group grid content-start gap-3.5">
<img src={featured.cover} alt="" className="mb-2 aspect-[16/10] w-full rounded-xl bg-primary-soft object-cover" />
<div className="flex flex-wrap items-center gap-2.5 text-[13px] text-fg-subtle">
<span className="rounded-full bg-primary-soft px-2 py-0.5 text-xs font-medium text-primary-soft-fg">Featured</span>
<span>{featured.tag}</span><span aria-hidden>·</span><span>{featured.minutes} min read</span>
</div>
<h3 className="text-balance text-2xl font-semibold leading-tight tracking-tight text-fg group-hover:underline group-hover:underline-offset-4 sm:text-[28px]">{featured.title}</h3>
<p className="text-[15px] leading-relaxed text-fg-muted">{featured.excerpt}</p>
<p className="text-[13px] text-fg-subtle">{featured.author} · <time dateTime={featured.dateTime}>{featured.date}</time></p>
</a>
<div>
<div className="mb-[18px] flex items-center justify-between">
<h3 className="text-[13px] font-semibold uppercase tracking-wider text-fg-subtle">Recent</h3>
<a href="/blog" className="inline-flex items-center gap-1 text-[13px] font-medium text-primary hover:underline">All posts <ArrowRight className="size-3.5" aria-hidden /></a>
</div>
<ul className="divide-y divide-border">
{recent.map((p) => (
<li key={p.href}>
<a href={p.href} className="group grid grid-cols-[88px_1fr] items-center gap-4 py-[18px] first:pt-0">
<img src={p.cover} alt="" className="aspect-square w-full rounded-lg bg-surface-2 object-cover" />
<div className="grid gap-1.5">
<p className="text-[15px] font-semibold leading-snug text-fg group-hover:underline group-hover:underline-offset-4">{p.title}</p>
<p className="flex flex-wrap gap-2 text-[13px] text-fg-subtle"><span>{p.tag}</span><span aria-hidden>·</span><time dateTime={p.dateTime}>{p.date}</time></p>
</div>
</a>
</li>
))}
</ul>
</div>
</div>
</div>
</section>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.