Software

wellbelove.org

Overview

This site. An Eleventy-generated static site on GitHub Pages with DecapCMS for content editing and a Cloudflare Worker handling GitHub OAuth.

wellbelove.org is my personal site — portfolio, blog, project docs, and everything else I want to put my name on. It's built with a small, deliberately boring stack: static generation, git-backed content, no database, no server-side code. The whole thing deploys on every push to main, plus once a day via a scheduled GitHub Action so the live GitHub stats on the homepage don't go stale.

Why this exists: I wanted somewhere I fully owned — no Medium, no Substack, no hosted CMS pricing model — where I could write, publish project docs, and demonstrate that I think about architecture decisions for my own work the same way I think about them for clients.

Stack

Decision Records

Static generation over a framework with runtime

I considered Next.js and Astro. Both are capable, both have features I don't need. Eleventy does one thing — turn templates and markdown into HTML — and does it without shipping JavaScript to the browser by default. For a portfolio site with a blog and a handful of project pages, runtime rendering adds complexity without benefit.

GitHub Pages over Cloudflare Pages or Vercel

Cloudflare Pages has faster builds and edge functions. Vercel has better DX. I went with GitHub Pages because the site's content lives in a GitHub repo, the deploy target being the same platform removes a moving part, and the free tier is more than adequate. When I outgrow this — if I ever do — the static output is portable to either in minutes.

DecapCMS + Cloudflare Worker for OAuth

DecapCMS edits content by committing to the repo, so there's no database and no hosted CMS to pay for or migrate off. Its only gap is auth: it needs an OAuth handshake with GitHub, and GitHub won't let you do that from a static site. The traditional answer is a small server (Netlify Identity, a Node service, etc.). Instead, the OAuth proxy runs as a single Cloudflare Worker — free tier, cold-start in milliseconds, one file of code.

Single repo for site + project docs

Each of my public projects (domain-security-toolkit, wblv-private-cloud-lab, ats-cvc) is its own GitHub repo. Originally each had its own GitHub Pages deployment under a different path. That got messy — three deployments, three sources of truth for docs, and the URLs didn't line up. I consolidated everything under wellbelove.org and disabled Pages on the project repos. Now the canonical documentation for each project lives here, next to the portfolio and blog, and the project repos stay focused on code.

Build-time GitHub data, daily cron for freshness

The homepage shows live GitHub stats — commits, active days, last push, most recent repo. I fetch that data at build time via GitHub's REST API and bake it into the static output. Client-side fetches would add a loading state, an API call per visitor, and a dependency on the API being reachable. Baking it in keeps the site fully static. To stop the data going stale between pushes, a GitHub Action rebuilds the site once a day at 06:00 UTC, which catches activity from the project repos too.

Per-repo /commits endpoint instead of public events

I originally used GitHub's public events API for the commit count, but it's capped at 300 events over 90 days and strips commit-count fields from push events. That produced inaccurate totals — active days where the homepage still said "0 commits". I switched to calling /repos/{owner}/{repo}/commits?author={user}&since=... per public repo and aggregating the results. One extra API call per repo, but the counts are real.

Per-project docs sidebars, not a unified one

Each project's docs page has its own sidebar listing the H2 and H3 headings of that page, populated at runtime by JavaScript. I considered a shared sidebar listing every project's sections — the Cloudflare-docs model — but it would have required users to scroll through unrelated projects to find what they wanted. The per-project sidebar matches how people actually read docs: pick a project, then navigate within it. A "← All projects" link lives at the top of each sidebar for wayfinding.

Frontend Architecture

CSS approach

Two stylesheets — style.css for the main site and docs.css for the docs layout. No framework, no Tailwind, no preprocessor. CSS custom properties hold the design tokens (colours, font stacks, spacing scale) so the theme can change from one file.

Inline styles are used liberally on one-off elements (page-specific layouts, hero sections) and factored into classes only when a pattern repeats. It's a deliberate trade-off — a little duplication in exchange for not having to maintain a large class vocabulary for a small site.

Responsive grids

The homepage and projects page use grid-template-columns: repeat(auto-fit, minmax(min(100%, Npx), 1fr)) with min-width: 0 on every grid item. Two deliberate choices:

CSS cache-busting

Stylesheet <link> tags include a manual version query string (?v=2026-04-05-e). When I ship CSS changes I bump the version. Without it, Cloudflare's CDN and GitHub Pages' cache happily serve stale CSS to visitors whose browsers have cached the unversioned URL — which bit me during the mobile-layout rebuild when fixes looked broken because the new CSS wasn't reaching the browser.

Accessibility

The site targets WCAG 2.1 AA. Specific things I did rather than leave to luck:

SEO & shareability

Known Gaps

Repo

The full source is on GitHub: wblv-dev/wblv-dev.github.io. Pull requests welcome if you find something broken.