Personal developer blog for Reid Marlow. Built with Astro. Design: calm indie-dev, e-ink warm (light) ↔ soft dark, monospace + green accents.
- Live: https://reidmarlow.com
- Stack: Astro 7 · Hashnode (headless CMS) as the single content source · RSS · sitemap · zero client JS except the theme toggle.
- Internal codename:
komo(repo/project names may still say komoai-live; never the public brand).
Posts are written in Hashnode and pulled at build time over the Hashnode
GraphQL API (gql-beta.hashnode.com, Bearer PAT). reidmarlow.com is the only public
front-end; Hashnode handles the editor, image hosting, drafts and scheduling.
Hashnode (write/publish)
└─ GraphQL ──build-time──▶ Astro content loader (src/content.config.ts)
└─▶ reidmarlow.com/<slug> (public domain + front-end)
└─ Deploy hook (publish/update) ──▶ Vercel rebuild
Build-time env (see .env.example): HASHNODE_TOKEN (required, Pro PAT),
optional HASHNODE_PUBLICATION_ID (preferred), optional HASHNODE_PUBLICATION_HOST
(Hashnode CMS host metadata — defaults to reidmarlow.com). Set them as Vercel
Project env vars for production builds; locally they come from .env. Without a
token the site builds with an empty blog (graceful empty states) rather than failing.
npm install
npm run dev # http://localhost:4321
npm run build # output to ./dist
npm run preview # preview the production buildsrc/
├─ consts.ts # site title, description, nav, socials, author info
├─ content.config.ts # Hashnode GraphQL loader + blog collection schema
├─ styles/global.css # the whole design system (theme vars + components)
├─ components/ # BaseHead, Header (+ theme toggle), Footer, FormattedDate
├─ layouts/
│ ├─ BaseLayout.astro # html/head/body shell, header + footer
│ └─ BlogPost.astro # single-post article layout
└─ pages/
├─ index.astro # homepage (hero · whoami · latest · blog)
├─ about.astro # About
├─ blog/index.astro # blog archive
├─ [...slug].astro # renders each post (root-level → /<slug>/)
└─ rss.xml.js # RSS feed
Write and publish in Hashnode (set the slug, SEO title/description, cover image
and tags there). On publish, fire the Vercel deploy hook; the new build pulls the
post via GraphQL and it appears at /<slug>/. The Hashnode slug becomes the URL
slug. The homepage, blog archive and RSS feed pick it up automatically; with no
posts the site shows empty states instead.
To preview locally with real content, put a HASHNODE_TOKEN in .env and run
npm run build / npm run preview.
Light/dark is toggled in the header and remembered in localStorage (komo-theme),
applied before paint to avoid a flash.