---
title: "How It's Made - The Blog"
date: "2025-12-22"
excerpt: "A cynic walks you through building a blog with SvelteKit, particles.js that doesn't work, and enough utility classes to make you start questioning your choices."
image: "/images/posts/jawhng.png"
---

**Everybody's dying.** Some people are just doing it faster than others by choosing the wrong JavaScript framework.
Me... as an Angular developer I'm here for a good time, not a long time. Now Svelte - that's something else. Long before I became a developer I used to have a blog. *That* blog was made using Jekyll and lived as a GitHub page on a github.io domain. *This* blog is an almost exact visual copy of the old one. 
I loved the setup: I would write a post in markdown, host any images on a 3rd party and just **git commit -> git push** and that was that. I've tried to replicate this setup in a way that would teach me more Svelte, even if it ends but being a little overcomplicated. 
It's a work in progress. Here's some info for nerds, colleagues or prospective colleagues. If you're neither of these - pay me more. 

## The Tech Stack

Here's what powers this monument to narcissism you're currently reading:

- **SvelteKit** - Because apparently React wasn't complicated enough, and Vue was too mainstream. At least Svelte 5 has runes now, which sound mystical but are really just state management with a rebrand.
- **MDsveX** - For simple markdown and then maybe a component when you don't want to keep things simple.
- **Tailwind CSS v4** - Utility classes everywhere. My HTML looks like someone had a seizure on the keyboard, but hey, at least I don't have to name things.
- **TypeScript** - Strict mode enabled, naturally. Because nothing says "I hate myself" quite like adding type annotations to everything.

## The Font Situation

I spent more time picking fonts than actually building this blog. By a lot.

## The Particles Background (A Tragedy in Three Acts)

**Act 1:** "My last blog used particles.js. It's been 11 years since its last update but how bad could it be?"

**Act 2:** `Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode`

**Act 3:** Install tsparticles. Realize the Svelte wrapper expects Svelte 4. Remove wrapper. Import engine directly. Finally works. Question choices.

The particles are approximately what I felt to be my favorite color on that day (`#9000f5` to be exact). There's a subtle blur overlay so they don't distract from the profound thoughts I'm sharing with the world.

## The Routing Architecture

Blog posts live at `/posts/[slug]` because that's what civilized people do. The homepage is the blog listing because if you came here expecting anything else, you're lost. At some point these posts could become too many (right...) so I might have to add pagination. 

There's also an About page and a Music page. Both are markdown files. Both are probably empty. I'll get to it ...eventually.

## Key Learnings

1. **Old libraries don't work with modern tooling** - Shocking, I know. It's almost like maintaining software requires effort.

2. **The most important file is CLAUDE.md** - Because six months from now, Future Me will have no idea how any of this works. Documentation is self-care.

## The Build Process

It's using `@sveltejs/adapter-static` because this gets deployed to static hosting. No server-side rendering, no edge functions, no complexity. Just HTML, CSS, and JavaScript being served like it's 2005.

## The Deployment Pipeline (A Journey Through Pain)

**Initial Plan:** Set up Gitea Actions for that sweet CI/CD professionalism. GitHub Actions syntax, portable workflows, something to show employers.

**Reality Check:** The runner lives in Docker. Docker doesn't have access to the host's nvm. The host has nvm. The runner doesn't care. Workflow hangs on "Setting up Node.js" forever. Abandon ship.

**Current Solution:** A Python Flask webhook receiver that:
1. Listens on port 9001 (port 9000 was taken by something I'll never identify)
2. Gets a POST from Gitea when I push to `main`
3. Runs a bash script that has more environment variable exports than actual logic
4. Responds with 200 OK immediately because Gitea has commitment issues and times out after 10 seconds

The deployment script sources nvm, adds pnpm to PATH (because it lives in `~/.local/share/pnpm` for reasons), pulls the latest code, runs `pnpm install && pnpm run build`, and copies everything to an nginx container.

It runs in a background thread because build times are longer than webhook timeouts. Logs go to both systemd journal and a file because Python's stdout buffering makes systemd sad.

**Total complexity:** Higher than just SSHing in and running a script manually.

**Total convenience:** Also higher than just SSHing in and running a script manually.

**Conclusion:** Worth it? Ask me in six months when I've forgotten how it works and have to debug it at 2 AM.

## Conclusion

This site was built in a couple hours with SvelteKit, some questionable design choices, and the crushing realization that web development in 2025 is somehow both easier and more complicated than it was 5 years ago.

Will I maintain it? Probably not. Will I write actual content? Maybe. Will I spend more time tweaking the particle configuration than writing posts? Absolutely.
