Skip to content
AstroCraft Docs
On this theme

Health Library

The health library is Medice’s blog, and it is named for what it is rather than for what the route usually is. It lives at /resources/health-library/ with ten articles, and it is the one part of the theme where the content is a collection rather than a config file.

Everything about the schema is in Content Collections. This chapter is about what gets rendered.

Two routes

src/pages/resources/health-library/index.astro draws the index: a featured card, a filter bar and a grid.

src/pages/resources/health-library/[post].astro draws one article. It emits BlogPosting schema and a BreadcrumbList beside the site graph, sets og:type to article, and passes article:published_time, article:modified_time and article:author through BaseHead.

Both read src/js/blog.ts rather than the collection.

The byline comes from the roster

An article’s frontmatter names an author with a doctor slug and nothing else. The byline the page draws — name, then credentials · specialty · clinic, with the clinician’s photograph — is read from src/config/doctors/<slug>.ts at build time, and the name links to that clinician’s profile.

This is the payoff of not having an authors collection. The byline cannot go stale, the photograph cannot be missing, and the profile link cannot 404, because all three are the same nine files the directory renders. The “medically reviewed by” note is the same lookup.

What the index computes

The featured card is the newest published post — the head of the date sort, not a flag. It carries a “N sources cited” fact that counts the sources array, so it is right by construction.

Every card shows a reading time counted from the body. Nothing stores it.

The category chips and the filter select both read facetOptions(), which derives the option list from the posts that actually exist rather than from a hand-written list that could offer a category with nothing behind it.

The index uses the same in-browser listing filter as the specialty grid and the clinician directory: a search box, a select, a row of shortcut chips, a live count and an empty state, all operating on cards already in the DOM. Nothing is fetched and no route is navigated.

The filter is worth one specific note. The featured card draws an “All heart health articles” pill, and there is no per-category route for it to point at. Rather than ship a control that cannot move anything — the shape this codebase drops on sight — the facet system accepts a query parameter that preselects a control on load, so ?category=Heart+Health lands on the index with that filter already applied. The link narrows something real.

The shared machinery is src/components/Sections/Global/_listing.ts, which is pure and has _listing.test.ts beside it, split from _listingFilter.ts, which touches the document. See Routing for the same pattern on the other two listings.

RSS

src/pages/rss.xml.ts is a hand-rolled, escaped RSS 2.0 endpoint — no @astrojs/rss dependency, consistent with the rest of the SEO layer being owned rather than vendored. It reads getPosts(), so it agrees with the index about what “published” means, and its absolute URLs derive from site.

The feed is linked from BaseHead and listed in llms.txt.

Article body conventions

Articles are .mdx. Two things are built from the body rather than declared:

The “On this page” list comes from the article’s own ## headings. Write your sections as ## and the list follows; there is no separate table-of-contents field to keep in step.

Reading time is counted from the body text.

The sources band renders only when the frontmatter carries a sources array. It is optional and absent on most shipped articles on purpose — a fabricated citation is worse than no citation, so the band appears only where a real, checkable reference list exists. If you are replacing the sample content with real medical writing, this is the field to take seriously.

Category and author lists

Both live in src/config/blogData.json.ts, which also holds the chrome — the strings every article draws identically, kept out of frontmatter because a string that is byte-identical in every entry of a collection is chrome in the wrong file.

To add a sixth category, add it there; the schema’s enum and the filter’s options both follow. To add an author, add a clinician to src/config/doctors/ — the author list is derived from the roster, so there is nothing else to update.

Adding an article

Create src/data/blog/<slug>/index.mdx with a cover image beside it, fill the frontmatter, and it appears — on the index, in the feed, in llms.txt, and in the homepage band if it is recent enough. There is no list to register it in.

NEXT STEPServices