Source of smallobservations.net — a web notebook of street art I've enjoyed.
A Hugo build with one local theme written from scratch. No JavaScript, one stylesheet, one self-hosted variable font (Fraunces), no third-party requests. Photos run through Hugo's image pipeline at build time to produce a WebP + JPEG srcset per post.
A small Python tool that turns a phone photo into a Hugo post. For each image in _ingest/, it reads EXIF (date, GPS, camera) with Pillow, reverse-geocodes the coordinates against OpenStreetMap Nominatim, builds a YYYY-MM-DD-<city> slug, resizes the JPEG to 1600px on the long edge, and writes the post with stable front matter. Originals move to _ingest/_processed/ and never enter git; the resized JPEGs do. Nominatim calls are rate-limited to their 1 req/sec policy and cached on disk by rounded coordinates.
Everything goes through make — never plain hugo (it leaves orphans in public/).
| Command | What it does |
|---|---|
make dev |
Local dev server |
make build |
Clean production build |
make check |
Build + content / accessibility / HTML audits |
make ingest |
Process new photos in _ingest/ |
make test |
Run ingest tests |
make deploy |
Build and push to S3 + CloudFront |
make stats |
Generate weekly traffic report from logs |
make check builds the site and runs a battery of checks.
Content shape (Python scripts in scripts/):
- Front-matter schema on every photo post
- Slug uniqueness across
content/posts - Alt text present on every photo
- Rendered-page metadata: canonical URL, OG tags, taxonomy links
- RSS feed and sitemap URL contracts
Standards / accessibility (third-party CLIs):
- pa11y — WCAG 2.1 AA on the homepage and a random post page
- htmltest — internal link checker; also flags empty
<img>alt - vnu — the W3C HTML validator
xmllint— well-formedness check onsitemap.xmlandfeed.xml
Lighthouse isn't wired in — local Hugo perf numbers aren't representative of the deployed site.
make deploy uses Hugo's built-in deployer to push to an S3 bucket in eu-west-2 fronted by CloudFront, and invalidates the changed paths. The apex domain (smallobservations.net) is the only published host.
Cache headers are set per-file in hugo.toml:
- Images and fonts get one-year
max-age— Hugo fingerprints their filenames, so a new render is a new URL. - HTML, the RSS feed, and the sitemap revalidate on every request, so a deploy is visible immediately.
- AVIF isn't used (Hugo extended on macOS doesn't ship with libavif)
- URL year/month come from the photo's taken date, not the publish date
- Commit messages are validated by a git hook (conventional-commits subset)
This project is built collaboratively with AI coding agents — primarily Claude Code, with Codex used for second opinions and parallel work. The process leans on the Superpowers skill set: specs in docs/superpowers/specs/, implementation plans in docs/superpowers/plans/, both checked in. Visual work was prototyped with the frontend-design skill and the brainstorming skill's visual companion; canonical mockups live in mockups/.