--- import { getCollection, render } from 'astro:content'; import Base from '../../layouts/Base.astro'; import { fmt, readMin as readMinOf } from '../../lib/news.js'; export async function getStaticPaths() { // Date-sorted so each article knows its sequential neighbours (prev/next // pattern of every editorial site — keeps readers in the content flow). const posts = (await getCollection('news', ({ data }) => !data.draft)).sort( (a, b) => b.data.date.valueOf() - a.data.date.valueOf(), ); return posts.map((post, i) => ({ params: { slug: post.id }, props: { post, newer: posts[i - 1] ?? null, older: posts[i + 1] ?? null }, })); } const { post, newer, older } = Astro.props; const { Content, headings } = await render(post); const toc = headings.filter((h) => h.depth === 2 || h.depth === 3); const readMin = readMinOf(post); --- {/* Reading progress (scroll-driven CSS, no JS) — styled only where animation-timeline is supported; elsewhere it stays an empty div. */} {/* TOC pattern (MDN/Stripe/NN-g): sticky right rail ≥1200px, collapsed
under the title below that. CSS shows exactly one of the two. */} {/* lang sits on the article, not : the chrome (nav/footer) stays English for screen readers even when the article body is not. */}
← All news {/* Editorial anatomy (Guardian/BBC/Reuters convention): headline → standfirst → byline/meta → lead image → body. The standfirst is the description made visible — cards already show it, the article should too. */}

{post.data.title}

{post.data.description}

{/* Meta — one quiet muted line: category · author · date · reading time */}
{post.data.category}· {post.data.author}· · {readMin} min read
{post.data.cover && } {/* TOC sits between lead image and body (Wikipedia/GOV.UK contents position): the press head above stays unbroken, and the list is adjacent to the content it indexes. */} {toc.length >= 3 && (
On this page
    {toc.map((h) => (
  1. {h.text}
  2. ))}
)}
{(newer || older) && ( )}
{toc.length >= 3 && ( )}