2 min read

Hello, new site

I had a single-page HTML portfolio for a couple of years. It did its job — told people what I make, gave them a way to reach me. But it had no room for the thing I actually wanted to do: write.

Why now

Most of what I figure out — patterns that worked, things that didn’t, small techniques worth keeping — ends up in scratch files and DMs. I want a place to put it down, both for me and for anyone who might find it useful later.

What this is

  • Work — selected projects, shipped and otherwise.
  • Notes — short pieces on whatever I’m currently building.
  • Contact — boring but necessary.

That’s it. No newsletter. No “subscribe” modals. No tracking.

How it’s built

The site is Astro, generating static files. Each note is a Markdown file in src/content/notes/. Push to git, the deploy fires, the new post is live a minute later. No CMS, no database, no auth, no backend.

The interesting parts:

  • Search runs entirely in the browser via Pagefind. At build time it indexes every page; at runtime it loads a tiny WASM blob and returns instant local results. No server, no API key, no quota. Tags in any note are clickable and pre-fill the search.
  • Code blocks go through Shiki with dual light/dark themes baked into the HTML. Adding {2,4-6} after the language tag highlights those lines; a diff block colors removed and added lines red and green. Line numbers come from a CSS counter. No client JS.
  • OG images are rendered per post at build time using Satori — JSX in, PNG out. Every post gets a unique preview card when shared.
  • Markdown extensions — GitHub-style callouts (> [!NOTE]), footnotes, anchor links on headings, external-link favicons and arrows, a reading-progress bar, a sticky table of contents on long posts. A few small plugins, none of them aware of the others.
  • Type and colorGeist and Geist Mono, one sans-and-mono pair for everything. The mono carries dates, code, and small labels; the sans does the rest. Warm stone neutrals with a single lime accent. Both themes defined as CSS variables; toggling flips a data-theme attribute on the root — no JS theme library, no flash on load.
  • Hover-preview on any internal note link — pulls a one-paragraph excerpt from a JSON map built alongside the index. Wikipedia-style, but with the site’s typography.
  • Schema-checked content via Zod — a typo in a frontmatter field fails the build before it can break a page.

The palette:

:root {
  --bg:     hsl(60 9% 98%);
  --fg:     hsl(24 10% 10%);
  --accent: hsl(82 78% 34%);
}

:root[data-theme='dark'] {
  --bg:     hsl(20 14% 4%);
  --fg:     hsl(60 9% 98%);
  --accent: hsl(79 76% 56%);
}

The publishing flow:

src/content/notes/this-post.md   # write
git push                          # → live

That’s the whole loop. Source is on GitHub.

More soon.