Refactoring My Personal Site

I spent the last month refactoring my personal site. What started as "make it faster" turned into a month-long spree that replaced the Express + SQLite backend with a fully static Eleventy build, added five visual themes, and set up a GitHub Actions deploy pipeline. All of it, naturally, vibe-coded.

My old site was built on Express with Handlebars templates and SQLite3 for blog content. It worked for years, but the architecture had grown stale: dynamic server rendering pages on every request, Bootstrap 3.3.2, jQuery 1.10.2, SQLite queries for content that rarely changed. The tipping point was realizing I didn't have to do all the grunt work to overhaul the site — no caching, no CDN, no reason to be dynamic.

I migrated everything to Eleventy. Handlebars to Nunjucks, SQLite to Markdown files with YAML frontmatter. Ported over 15+ years of blog posts across three categories — programming, cooking, and AI — plus all the legacy project pages (imageprocjs, raytracer, minesweeper, fractal). The Eleventy config is minimal: three collections, one plugin (syntax highlighting), and a passthrough copy for static assets. The build outputs to _site/, and a GitHub Action rsyncs the result to phg1024.github.io.

module.exports = function(eleventyConfig) {
  eleventyConfig.addPlugin(syntaxHighlight);
  eleventyConfig.addPassthroughCopy({
     "public/css": "css",
     "public/images": "images",
     "public/projects": "projects",
   });
   return { dir: { input: "src", output: "_site" } };
};

That's it. The entire build config.

The fun part was replacing the single visual identity with five complete themes, each defined entirely through CSS custom properties:

  • Minimalist — Clean, muted earthy tones with soft shadows
  • Cyberpunk — Neon gradients, glow effects, mouse-tracking backdrop
  • Matrix — Green-on-dark with terminal-style aesthetics
  • Industrial — Steel blue-gray palette with subtle grid textures
  • Windows 3.1 — Teal background, 3D beveled borders, MS Sans Serif

Each theme redefines ~30 CSS variables (backgrounds, surfaces, borders, text colors, accents, shadows, font families). The template structure stays the same; only the variable values change.

/* Each theme is ~30 CSS variables. Example: */
[data-theme="matrix"] {
   --bg: #020703;
   --surface: rgba(6, 18, 9, 0.8);
   --border: rgba(96, 255, 128, 0.22);
   --text: #d8ffd8;
   --accent: #7cff9d;
   --accent-glow: rgba(124, 255, 157, 0.34);
   --sans: "Courier New", "Lucida Console", monospace;
}

The theme selector lives in the footer. The chosen theme is saved to localStorage and applied via an inline script before the page even paints.

The cyberpunk and Matrix themes both feature a canvas-based backdrop that reacts to mouse movement. It uses CSS custom properties driven by mouse position — --pointer-x, --pointer-y, --pointer-glow — with radial gradients that follow the cursor. There's also a pointer-reactive-overlay layer with mix-blend-mode: screen for additional atmospheric lighting.

The deploy pipeline is simple but effective: push to master triggers the workflow, npm run build generates _site/, rsync -av --delete syncs to the deploy repo, auto-commit and push. The whole thing runs on ubuntu-latest with Node 20. No Docker, no caching layers, no artifact uploads. Just npm run build and rsync.

Looking at the commit history, most of the work falls into four buckets: migration (Express → Eleventy), layout polish (responsive grids, mobile carousels, image sizing), the theme system (CSS variable architecture, five visual identities, backdrop animations), and infrastructure (GitHub Actions deploy, jQuery upgrade, Gaussian fitting plan — still TODO).

The most time-consuming part wasn't the migration itself — it was the pixel-pushing. Getting the cyberpunk reading surfaces to have the right contrast, making the Matrix backdrop animations feel smooth, ensuring the Windows 3.1 theme actually looks like Windows 3.1 and not just "gray with rounded corners."

This was entirely vibe-coded. No PRs, no code reviews, no architecture documents. Just me, a theme selector dropdown, and way too many iterations on box-shadow values.

The site went from a dated Express app to a static site with five distinct visual identities, mouse-tracking canvas backdrops, and a fully automated deploy pipeline. All in about 25 commits over a month.

If you want to see the result, it's at phg1024.github.io. Try switching themes — the default is now the minimalist one, but the cyberpunk theme is my favorite.