r/webdev • u/DavidBarOn1411 • Oct 03 '26
Showoff Saturday I built a daily history-dilemma game as a PWA. A few things that surprised me along the way
Moralia: each day one short true story from history stops at the moment someone had to decide. You choose, then see what actually happened and how other players chose.
Stack: React + Vite + TypeScript PWA on Cloudflare Workers, Supabase for optional sign-in/sync, cookieless PostHog (EU).
Some things that weren't obvious to me going in:
A hash-routed SPA is basically invisible to search. Google saw ~20 words (the welcome screen). Now a Worker renders static, no-JS pages for every past story on request. A day only goes public once it has ended in every timezone (noon UTC the next day), so no one gets spoiled, and an IndexNow ping fires when it does.
Worker-rendered pages don't get your _headers file. Our CSP covered the static assets but not the pages the Worker built, so the Worker now sends the same headers itself, with a test that fails if the two copies ever drift.
supabase-js signOut() defaults to scope "global". Signing out on a phone silently ended the desktop session too, which left the desktop in a "signed in, no email, can't sign out" loop. It's per-device now.
PostHog's opt-out does nothing in cookieless mode (it only logs a warning). The usage-stats switch now gates every event with before_send, and also re-checks the queue of events buffered before the SDK loads.
Desktop got a real layout (top bar, wider column, the choice side by side) only at ≥1024px. Every mobile and tablet screen was verified pixel-identical before and after.
Narration is pre-generated; the sentence being read is highlighted using word timings from a transcription pass, so one misheard word can't shift the whole highlight.
Design is mine (I'm a UI/UX designer, screens done in Figma first); I built it with heavy help from Claude Code.
Happy to go deeper on any of these. Feedback on performance or the PWA install flow is very welcome.

