Demo Content: Using my readme for theme.
Wisdom Reboot: Ghost theme

The theme for jonathandutoit.com. It is built for reading: calm type, a narrow prose column, and a few tools for depth (Nutshells, footnote dots, a table of contents). There is no build step: edit the files, zip the folder, upload it.

Requires Ghost 6. npx gscan . should report "compatible with Ghost 6.x".

The rule

Ghost admin decides; the theme reflects. If a choice exists in Ghost settings (comments, sign-up access, paid membership, tiers, Portal plans, post access, fonts, navigation), the theme reads it instead of hardcoding it. The theme adds only what Ghost doesn't do.

Setup

  1. Upload the theme: Settings → Design → Change theme → Upload → Activate.
  2. Upload routes.yaml: Settings → Labs → Routes → Upload routes file. Ghost reads routes from there, not from the theme folder, so re-upload it whenever it changes.
  3. Typography: set both Heading font and Body font to "Theme default". The theme ships Fraunces (headings) and Lora (body) itself. Picking a font in Ghost overrides the theme, and Ghost then loads it from its own font service.
  4. Navigation: the primary list shows in the header, the mobile menu and the footer "Read" column; the secondary list shows in the footer "More" column. External links (such as the Substack letters) belong here too.
  5. Social links: Settings → Staff → your profile → Social links feeds the footer "Follow" column.

Create and publish these pages, with exactly these slugs. Each route reads its page and returns 404 if the page is missing, drafted or renamed. The page's title, excerpt and body become that route's heading, lede and intro.

Slug Route
essays /essays/
apps /apps/
projects /projects/
archive /archive/
tags /tags/
newsletters /newsletters/
membership /membership/

What lives where

URL Template Content
/ home.hbs Hero, the latest featured post, the six newest posts (no newsletters, no #unlisted), Letters feed
/page/2/… index.hbs Every post as cards
/essays/, /apps/, /projects/ page-*.hbs → partials/collection.hbs Card grid of one tag, 12 per page
/archive/ page-archive.hbs Every post with year headings, 50 per page; newsletters are labelled
/tags/ page-tags.hbs All writing except newsletters as a list, 15 per page, with the topics directory
/newsletters/ page-newsletters.hbs Every post tagged newsletters, 15 per page
/tag/<slug>/ tag.hbs One tag as a list, 12 per page
/membership/ page-membership.hbs Tiers from Ghost
/signin/, /signup/, /account/ Hand over to Ghost Portal

All listings use Ghost's own pagination (channel routes in routes.yaml), so there is no ceiling on how many posts they hold.

Post layouts

Post Layout
Primary (first) tag newsletters Newsletter: dated, narrow, sharing and sign-up at the foot, "More newsletters" (partials/newsletter.hbs)
Anything else Essay (partials/essay.hbs)
Post settings → Template → Toc Essay plus an "On this page" box built from h2/h3 headings
Post settings → Template → Wide Essay without the narrow column cap
Post settings → Template → Canvas No header, footer or prose column; the content brings its own styling

Emails are not rendered by the theme. Ghost builds every newsletter email from Settings → Newsletters.

Membership follows Ghost settings

Setting Effect
Comments on/off The comments section appears or disappears
Who can sign up The "Sign up" buttons and the newsletter subscribe box appear only while self-signup is allowed
Paid members on/off Paid tiers, "Upgrade" and plan management appear only while paid is on
Tiers (name, description, benefits, prices) /membership/ renders them as set; nothing is hardcoded
Portal plans (free, monthly, yearly) Only the offered periods get a price and a button. With both, a Monthly/Yearly switch appears
Post access Cards and lists show a lock only to readers who can't read that post; the post header names who it's for

Writing conventions (markdown card, web only)

Essays are written in a markdown card. These conventions are implemented in assets/js/main.js and exist only on the website. Newsletters use Ghost's own cards instead.

Write Get
> :pull The line. (optional last line > — Name) Pull quote
> :note Title then > Body Note callout
==text== Hand-inked highlight
[^1] and [^1]: Source Footnote dot that opens the note in place; the list stays at the foot
[:visible text](#x-some-id) and ### :x some-id Nutshell: the :x section is hidden until the link is chosen
[:text](/other-essay/#x-some-id) Nutshell from another post on this site

Nutshell sections go after the essay and before the footnotes. Each section runs to the next heading of the same or higher level, the next :x heading, or the footnotes.

Write a custom excerpt for posts that use Nutshells. Ghost's automatic excerpt is plain text from the top of the post, so it would show the leading : of a Nutshell link.

Tags with a job

Tag Effect
essays, apps, project Fill /essays/, /apps/, /projects/
newsletters Fills /newsletters/; as the primary tag it switches the post to the newsletter layout and keeps it off the homepage
#unlisted Keeps a post off the homepage (it still appears everywhere else)
#series <name> Makes a series. The slug must contain series- (Ghost makes hash-series-<name>). Set the tag's description to the series title

A tag's image (a 48×48 SVG works best) and accent colour become the card image for posts without a feature image.

Editing

  • CSS: assets/css/screen.css. Tokens are at the top, and dark mode only changes tokens. Colour never carries meaning on its own.
  • JS: assets/js/main.js. It has fifteen independent modules, listed at the top of the file.
  • Comments: describe the code as it is now; history goes in CHANGELOG.md. A comment that claims something about Ghost names its source (a docs page or a Ghost core file).
  • Fonts: assets/fonts/ (SIL Open Font License; licence texts included). They are declared in partials/head.hbs.

Smoke test after every upload

  1. A made-up URL says "That page isn't here."
  2. /page/2/ shows "All posts", not the hero.
  3. Scroll any page: the header stays at the top.
  4. A post alone in its tag shows no "Read next".
  5. A Toc post lists its headings and no :x headings; a Nutshell opens and Escape closes it.
  6. A footnote dot opens its note.
  7. /membership/ matches what Settings → Membership and Portal offer.
  8. A newsletter post shows its date and "More newsletters".
  9. Toggle dark mode in the footer; every page stays readable.

Licence

MIT. Fraunces and Lora: SIL Open Font License 1.1.