Skip to content
AstroCraft Docs
On this theme

Analysis & Blog

Indexa’s blog is ten posts and three bylines over two routes: the index at /blog/ and a post at /blog/<slug>/. There is no topic route, no author route and no pagination — the archive is one list, which is the right shape for ten posts and the wrong one for three hundred.

The index

Eight sections, composed in the route and fed from blogData.json.ts plus the collection itself:

Masthead → FeaturedReport → Topics → Toolbar → Archive → Ledger → Authors → AlertBand.

The featured report is chosen by slug in config, and the choice is asserted rather than assumed:

const featured = await getEntry("blog", blogData.featured.slug);
if (!featured) throw new Error(`blogData.featured.slug not found: ${blogData.featured.slug}`);

A renamed folder therefore fails the build with the bad slug printed, instead of rendering a page with a hole in it. The archive is every other post, newest first, so the featured post never appears twice.

Two numbers on that page are real: the toolbar’s count and the pagination line both carry posts.length. The editorial figures in the ledger and the topic pills are placeholders, consistent with the rest of the site’s index copy.

The post page

Six sections, two of them conditional:

<PostHeader post={post} />
<ArticleBody post={post}><Content /></ArticleBody>
{author && <AuthorBox author={author} />}
<Related posts={relatedPosts} />
<AlertBand />
{post.data.exit && <ExitBand {...post.data.exit} />}

Related is the three most recent other posts. ExitBand renders only when the post authors an exit block in its frontmatter — a per-post CTA band with its own heading, two links and a row of figures, so a review can end by sending you to the record and an index report can end by sending you to the hub.

ArticleBody styles the rendered markdown from the wrapper with arbitrary-child variants ([&_h2]:…, [&>p]:…) rather than a global .prose class. That keeps the house type scale inside the one component that renders article bodies, at the cost of a long class string in one place — a trade the file states.

The “In this article” rail

The rail on the right of an article is built from the post body at build time by extractH2s in src/js/toc.ts, and it only lists H2s that carry an explicit id:

<h2 id="where-augusts-cars-went">Where August's cars went</h2>

The markdown pipeline does not slug headings, so a plain ## Heading produces no DOM id. A table of contents generated from those headings would render a list of anchors that all go nowhere — which looks like a working feature and is not. So the extractor matches only the explicit form, and a post with no ids simply gets no rail. toc.test.ts pins that: an H2 with an id is listed, an H2 without one is not, and an H3 is never mistaken for either.

Bylines

authors is a Zod reference("authors") array, and the post page resolves the first entry for the author box. The three bylines live as collection entries with an avatar, a role, an about paragraph and a contact link, so the same author renders identically on a card, in a byline and on the blog index’s authors band.

RSS

/rss.xml is hand-rolled in src/pages/rss.xml.ts — about forty lines, no feed dependency, consistent with the icons, motion and SEO layers being owned rather than vendored. It lists every non-draft post newest first, escapes the five XML entities itself, and builds absolute URLs from site.

The feed is linked three ways: an alternate link in BaseHead (so a reader finds it from any page), a line in /llms.txt, and the footer’s Advice column.

What the reviews hub borrows

/reviews/ is not a second blog. It reads the same collection, filtered to posts whose categories include "Review", and presents them as verdicts with the hub’s own editorial framing around them. One post can therefore be both an article in the archive and a verdict on the shelf without being written twice — and reviewsData.test.ts fails if the hub names a post that is missing, draft or uncategorized. Browse Hubs & Index Pages has the rest of that page.

NEXT STEPRouting