No description
Find a file
Josh Bairstow d80cd255d4 fix(blog): make .is-hidden global, strip HTML tags from search text, add role=search
Final-review findings:

- .is-hidden was scoped to blog/index.astro, so it didn't match PostRow
  or YearHead roots (which carry their own component scope hashes). Net
  effect: the filter visibly only hid the featured article, while all
  archive rows + year heads stayed on screen. Wrap in :global() so the
  runtime-toggled class actually fires its display rule everywhere.
- buildSearchText regex stripped > but not <, so inline HTML open-tags
  like <span class="leadin"> leaked into data-search. Add < to the
  character class so the haystack stays plain text.
- Add role="search" landmark to the BlogSearch wrapper so screen-reader
  users can navigate to it directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 01:45:27 +10:00
docs docs: spec for light blog search 2026-06-01 23:45:06 +10:00
public/assets initial commit 2026-06-01 19:35:56 +10:00
src fix(blog): make .is-hidden global, strip HTML tags from search text, add role=search 2026-06-02 01:45:27 +10:00
.dockerignore initial commit 2026-06-01 19:35:56 +10:00
.gitignore initial commit 2026-06-01 19:35:56 +10:00
astro.config.mjs initial commit 2026-06-01 19:35:56 +10:00
Caddyfile initial commit 2026-06-01 19:35:56 +10:00
compose.yml initial commit 2026-06-01 19:35:56 +10:00
Dockerfile initial commit 2026-06-01 19:35:56 +10:00
package-lock.json initial commit 2026-06-01 19:35:56 +10:00
package.json initial commit 2026-06-01 19:35:56 +10:00
README.md initial commit 2026-06-01 19:35:56 +10:00
tsconfig.json initial commit 2026-06-01 19:35:56 +10:00

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.