The site is a flat vault of Markdown notes in Obsidian, rendered as a static
site, with no build framework on the pages themselves. This is how it works.
How it is built
- Quartz turns the Obsidian vault into a static site — note pages are one
reading column, no sidebar forest. Quartz is pinned to a specific commit, so
an upstream change can never change this site on its own. - The section pages (
/Notes,/Projects,/Practice-of-Product,/Travel,
/library,/news) are not Quartz pages. A post-build step,
build-pages.mjs, derives them from the notes and writes them as plain
HTML/CSS/JS. No framework, no bundler — each page is a file you can read top
to bottom, and nothing here required editing a note to make it work. - Everything a page shows is derived at build time from
/api/notes.json— tags, length bands, dates, backlinks. The vault itself is
never touched. - The top bar is static HTML on every page, so navigation works before any
script runs. JavaScript only powers the interactive parts: search, the light/
dark toggle, the tag map, and refreshing the Bluesky feed. - Fonts are self-hosted (
InterandIBM Plex Mono, served aswoff2
from this site). No request to a third party, no layout shift. - The faint topographical background is County Kerry, where I’m from. Its
contour lines come from real SRTM elevation data for the county, rendered as
an SVG and masked behind the content, tinted a light or dark grey to match
the theme so it stays in the background. - Deploys happen on every push: GitHub Actions runs the build and publishes to
Cloudflare. The only third-party script is Google Analytics, which loads
asynchronously.
The principles
The whole design sits on a few rules, written in my notes:
- Flat storage, layered reading. One folder of notes; structure lives in
the presentation, never the directory tree. Adding a view never moves a
file. - Derive before you demand. No view depends on metadata that does not
exist yet. If a view needs tags, it works with the tags already inline in
the notes. - Don’t fake authority. Weak signals are shown as what they are. A
timeline that dresses an import date up as an authoring date is worse than
no timeline. - A view is a URL. Every filtered or sorted state is a link worth sending
to someone. - Experiments are cheap, disposable, and public. They live under
/lab, get tried on the real site, then graduate or die. An experiment is
never allowed to become load-bearing before it graduates. - Native, not embedded. Third-party embed scripts are a tax on load time
and control. Prefer fetching a public API and rendering the result in the
site’s own type and colour.
Stated, not derived
Some of this is hand-maintained on purpose: the few things that cannot be
derived — the homepage’s home.json, the shelf’s books.json, the folder-level
“this is a theme, not a place” calls — are stated, and the build asserts the
invariants that would otherwise break silently (links resolve, every page is
tracked, the two halves of the site show the same bar).
The design reasoning, experiment log, and the sites that influenced this
project live further back in my notes. If you use any of it for your own site,
that is exactly the point.