Code review forward-proofing: - Early-return on event.isComposing so the / shortcut doesn't disrupt IME composition (multi-keystroke input methods). - Treat <select> as editable so / doesn't steal focus while a dropdown is open and the user is typing to seek. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| docs | ||
| public/assets | ||
| src | ||
| .dockerignore | ||
| .gitignore | ||
| astro.config.mjs | ||
| Caddyfile | ||
| compose.yml | ||
| Dockerfile | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
jb-website
The first-draft live site for joshbairstow.com — Astro 5 + React islands, styled to a quiet-luxury brand system in warm browns and ink. Serves the apex and three personal subdomains (blog., art., code.) from a single build via host-aware reverse-proxy routing.
The planning doc — including tech rationale, architecture, decisions log, and open questions — lives at docs/planning.md. Read that first if you want context. The canonical brand spec lives in docs/design-system/ (imported from a Claude Design handoff bundle); the "one base + one accent" rule and the visit-one / visit-three litmus test are load-bearing for design decisions.
What's here
src/
├── pages/
│ ├── index.astro apex home (stack layout)
│ ├── 404.astro on-brand missing-page
│ ├── blog/
│ │ ├── index.astro writing archive
│ │ └── posts/[...slug].astro post detail
│ ├── art/index.astro generative-art focus view
│ └── code/index.astro workshop placeholder
├── layouts/
│ ├── BaseLayout.astro topbar + grain + sprinkled marks + footer
│ └── PostLayout.astro editorial reading template
├── components/
│ ├── atoms/ Wordmark, Eyebrow, Mark, InkArrow
│ ├── molecules/ IndexRow, LatestLine, PostRow, YearHead
│ ├── organisms/ TopBar, SiteFoot
│ └── islands/ HeroZone, TideStudyCanvas (React)
├── content/posts/ markdown blog posts (Astro content collection)
├── styles/ colors_and_type.css + site.css
└── lib/urls.ts cross-subdomain URL helpers
Dev
npm install
npm run dev # http://localhost:4321
The Astro dev server collapses subdomain links to relative paths so a single localhost preview shows the cross-subdomain navigation working. In a production build the same helpers emit absolute https://blog.joshbairstow.com/... URLs.
Build
npm run build # outputs static site to dist/
npm run preview # serve the build locally
Deploy (containerised, target = Vultr Sydney VPS)
The repo ships a Caddy-fronted container that serves the static build directly with automatic HTTPS.
docker compose build
docker compose up -d
Caddy's site config (Caddyfile) maps host headers → path prefixes:
joshbairstow.com→/srv/blog.joshbairstow.com→/srv/blog/art.joshbairstow.com→/srv/art/code.joshbairstow.com→/srv/code/
Asset paths (/_astro/* and /assets/*) are re-rooted back to /srv from the subdomain blocks so they resolve regardless of which subdomain served the HTML.
To add another in-repo subdomain later, add a directory under src/pages/ and a matching block in the Caddyfile — that's it.
Writing a blog post
Add a .md file under src/content/posts/. Frontmatter schema (validated by Zod in src/content.config.ts):
---
title: A short title
date: 2026-03-12
tag: Field notes # optional
series: No. 19 # optional
standfirst: One sentence in italic display type, under the title.
description: SEO/OG fallback when standfirst is absent.
readingMinutes: 6 # optional
draft: false # excludes from list when true
---
Lead with {.lead} after a paragraph to apply the small-caps lead-in styling. The Astro markdown renderer handles the rest against PostLayout.astro.
What's deferred
- Blog search (Pagefind wiring) — see planning doc step 8.
- Auth middleware — needed when access-gated pages land.
- RSS, OG image generation, additional art pieces.
- Content for
code.(currently a workshop placeholder). - Side-hustle subdomains (signage, keycaps) — out of scope; live in their own containers behind the same proxy.
License
All rights reserved unless stated otherwise. Brand marks, type pairings, and prose are personal; please don't redistribute.