Skip to content

feat(pwa): rebuild offline support with Workbox and modernize install UX - #88

Merged
schwannden merged 1 commit into
mainfrom
fix/pwa
Jun 16, 2026
Merged

feat(pwa): rebuild offline support with Workbox and modernize install UX#88
schwannden merged 1 commit into
mainfrom
fix/pwa

Conversation

@schwannden

Copy link
Copy Markdown
Contributor

Why

The hand-written public/sw.js precached CRA-era paths (/static/js/bundle.js, /static/css/main.css) that 404 in this Vite build. cache.addAll rejected → the service worker install failed entirely → the app shell was never cached and offline was broken. The install experience was also weak: no offline navigation fallback, no maskable icon (Android clips it), no apple-touch-icon/Apple meta (poor iOS install), and an unlinked favicon.

What

Root-cause fix — vite-plugin-pwa (injectManifest)

  • New source SW src/sw.ts, compiled by the plugin, which injects the real hashed-asset precache manifest (self.__WB_MANIFEST).
  • Navigation offline fallback: NetworkFirst for navigations (online visitors/crawlers always get fresh prerendered HTML), falling back to the last-visited page, then a precached app shell.
  • Ported + generalized Supabase caching: StaleWhileRevalidate for all /rest/v1/ reads (now covers family prayers, not just weekly) and CacheFirst + bounded expiration for storage images.
  • Old public/sw.js and public/manifest.json removed; manifest is now generated by the plugin.

Cross-platform install polish

  • Maskable icons (192/512) with Android safe-zone padding; apple-touch-icon (180); Apple meta tags + 10 iOS splash screens; wired favicons; light/dark theme-color.
  • Manifest gains id/scope/lang/dir/categories, app shortcuts (Weekly/Family prayers), and install screenshots (narrow + wide).
  • In-app update toast ("new version · Reload") and an Install button via beforeinstallprompt. New i18n keys in both locales.
  • Assets generated by scripts/generate-pwa-assets.mjs (reuses existing puppeteer + cached Chromium; not part of CI).

Prerender/deploy safety

The Pages prerender pipeline is preserved: it already setBypassServiceWorker(true), createRoot is unchanged, and navigations are network-first so snapshots never serve a stale precached shell. Only index.html (the shell, used solely as the offline fallback) is precached; per-route snapshots are served fresh at runtime.

Verification

  • format:check, lint, build (the three CI gates)
  • build:static (sitemap + headless prerender): 139 ok, 0 failed; prerendered /prayers retains ~16k chars of real content + full PWA head
  • ✅ Built dist/sw.js precaches real hashed assets/*.js|css (0 CRA paths); all 18 manifest-referenced assets present in dist/
  • ⏳ Needs a real browser/device (cannot run headless here): Lighthouse "Installable" + offline-reload in Chrome DevTools, and iOS home-screen install (icon/splash/status bar)

Note: the splash/screenshot art uses the SIM logo/wordmark on the brand background as a baseline — swap in richer marketing screenshots later by re-running the capture if desired.

🤖 Generated with Claude Code

The hand-written public/sw.js precached CRA-era paths (/static/js/bundle.js,
/static/css/main.css) that 404 in the Vite build, so cache.addAll rejected and
the service worker never finished installing — offline was broken entirely.

Replace it with vite-plugin-pwa (injectManifest): Workbox injects the real
hashed-asset precache manifest, and a custom src/sw.ts adds a NetworkFirst
navigation handler with an app-shell offline fallback, StaleWhileRevalidate for
Supabase REST reads (now covering family prayers too), and a bounded CacheFirst
for storage images. Navigations stay network-first so online visitors and
crawlers always get the freshly prerendered HTML — the Pages deploy/prerender
pipeline is unchanged (SW is bypassed during snapshotting).

Make the installed app more useful and cross-platform:
- maskable icons (192/512) with Android safe-zone padding
- apple-touch-icon (180), Apple meta tags, and iOS splash screens
- wired favicons and light/dark theme-color
- manifest id/scope/lang/categories, app shortcuts, and install screenshots
- in-app "update available" toast and an Install button (beforeinstallprompt)

Assets are generated by scripts/generate-pwa-assets.mjs (reuses the existing
puppeteer + cached Chromium; not part of CI).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@schwannden
schwannden merged commit 7c935b3 into main Jun 16, 2026
2 checks passed
@schwannden schwannden mentioned this pull request Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant