A magazine front page with a headline list, a lead story and a thumbnail list that stack on phones. React and Tailwind CSS, no dependencies.
EditorialFrontPage.tsx
// Magazine front page: a headline list on the left, the lead story in the
// middle and a thumbnail list on the right (stacks on phones).
// Pure Tailwind, no dependencies. Works with Tailwind v3 and v4.
type Story = { title: string; excerpt?: string; image?: string; href?: string; date?: string };
type Props = {
lead?: Story;
headlines?: Story[];
thumbs?: Story[];
};
const img = (id: string, w = 1200) => `https://images.unsplash.com/photo-${id}?w=${w}&q=80&auto=format&fit=crop`;
const DEFAULT_LEAD: Story = {
title: "The quiet islands still worth the journey",
excerpt: "Ferries, fishing villages and not a cruise ship in sight: our writers pick the Mediterranean's last calm corners.",
image: img("1783442799263-65dfd26d1813"),
};
const DEFAULT_HEADLINES: Story[] = [
{ title: "Inside the restaurant with a two-year waiting list", excerpt: "Twelve seats, one chef and a menu that changes daily." },
{ title: "Why everyone is moving to the coast", excerpt: "Remote work has redrawn the map of where people live." },
{ title: "The return of the overnight train", excerpt: "Sleeper routes are back, and they're fully booked." },
];
const DEFAULT_THUMBS: Story[] = [
{ title: "A long weekend in Monaco", image: img("1581819896533-f8ab6767ce7e", 300), date: "Mar 12" },
{ title: "Positano after the crowds", image: img("1561956021-947f09ae0101", 300), date: "Mar 10" },
{ title: "Dubrovnik's old town at dawn", image: img("1414862625453-d87604a607e4", 300), date: "Mar 08" },
{ title: "The Golden Gate in fog", image: img("1501594907352-04cda38ebc29", 300), date: "Mar 05" },
];
export default function EditorialFrontPage({ lead = DEFAULT_LEAD, headlines = DEFAULT_HEADLINES, thumbs = DEFAULT_THUMBS }: Props) {
return (
<section className="bg-white px-4 py-12 font-['Manrope',ui-sans-serif,system-ui,sans-serif] text-black antialiased md:px-8 md:py-16">
<div className="mx-auto grid max-w-6xl gap-8 md:grid-cols-2 lg:grid-cols-4">
<article className="group md:col-span-2 lg:col-start-2 lg:row-start-1">
<a href={lead.href ?? "#"} className="block">
<div className="relative aspect-[484/345] overflow-hidden bg-[#dadada]">
{lead.image && <img src={lead.image} alt="" className="absolute inset-0 h-full w-full object-cover" />}
</div>
<h2 className="mt-5 text-2xl font-bold group-hover:underline md:text-3xl">{lead.title}</h2>
{lead.excerpt && <p className="mt-2.5 text-[#727272]">{lead.excerpt}</p>}
</a>
</article>
<div className="lg:col-start-1 lg:row-start-1">
{headlines.map((s, i) => (
<article key={s.title} className={`border-b border-[#9f9e9c] pb-5 ${i > 0 ? "pt-5" : ""}`}>
<a href={s.href ?? "#"} className="group block">
<h3 className="text-lg font-bold group-hover:underline">{s.title}</h3>
{s.excerpt && <p className="mt-2.5 text-[#727272]">{s.excerpt}</p>}
</a>
</article>
))}
</div>
<div className="lg:col-start-4 lg:row-start-1">
<ul className="flex flex-col gap-5">
{thumbs.map((s) => (
<li key={s.title}>
<a href={s.href ?? "#"} className="group flex gap-4">
{s.image && <img src={s.image} alt="" loading="lazy" className="h-20 w-20 shrink-0 object-cover" />}
<div>
{s.date && <p className="text-xs uppercase text-[#727272]">{s.date}</p>}
<h3 className="mt-1 font-bold leading-snug group-hover:underline">{s.title}</h3>
</div>
</a>
</li>
))}
</ul>
</div>
</div>
</section>
);
}
How to use it
- Make sure your Next.js or React project has Tailwind CSS set up (v3 or v4 both work).
- Create
components/EditorialFrontPage.tsxand paste the code. - Import it into a page and replace the sample text and images with your own.
