Case study

Career Portal

The site you are reading: a zero-dependency vanilla-JS PWA with 90+ automated tests, Lighthouse 95+, and $0/month hosting on Cloudflare Pages.

web · pwa · March 2026

  • Vanilla JS (ES6)
  • CSS custom properties
  • PWA + Service Worker
  • node --test
  • Cloudflare Pages
  • Azure Functions

Context

Portfolio sites tend to be either a static template (forgettable) or a framework app with 200 KB+ of JavaScript and a build pipeline. I wanted the site itself to be the demonstration: fast, accessible, offline-capable, and readable source — with no framework tax.

Architecture

There is no build step. HTML pages import ES modules straight from /js/, styling is plain CSS with design tokens as custom properties, and everything ships as-is to Cloudflare Pages. The contact form posts to an Azure Function; the rest is static.

The first version's hook was a working terminal emulator with 28 commands. This redesign retired it for a content-first layout with a Ctrl+K command palette — the terminal was fun, but it hid the actual work behind a help command. One convention survived every version: every dynamic string passes through escapeHtml() before any innerHTML assignment.

Key decisions & tradeoffs

  • Zero build step. Files ship exactly as written — no bundler, no transpiler. The cost: no tree-shaking, so every module is hand-curated for size.
  • Native test runner. The suite runs on node --test — no Jest, no Mocha. GitHub Actions runs it on every push and blocks the Cloudflare Pages deploy on any failure.
  • Accessibility as a gate, not a feature. Skip links, landmark structure, full keyboard navigation, and prefers-reduced-motion honored by every animation (WCAG 2.1).
  • Manually versioned service worker. The PWA cache uses a fetch-first strategy with an offline fallback page; the cache name must be bumped by hand on asset changes. I have forgotten twice — the stale-cache bug is subtle both times.
  • Terminal → palette. The 28-command terminal buried the content, so v2 keeps the personality in a Ctrl+K palette and puts the case studies first. Try it.

Measured outcomes

Shipped dependencies
0
npm packages in the bundle
Automated tests
90+
native Node runner
Lighthouse
95+
performance & accessibility
Hosting cost
$0/mo
Cloudflare Pages free tier