v0.1.12 — PWA + a11y polish
Phase 2 of the multi-phase polish plan. Closes Theme C — issue #18.
Service worker
Orrery now installs as a true PWA on Android + iOS, with offline-first behaviour after first visit.
- Plugin: `@vite-pwa/sveltekit` (Workbox-generated SW). New devDependency.
- Cache strategies: shell + textures + fonts + logos + images precached (cache-first, immutable); mission JSON + i18n overlays stale-while-revalidate (instant from cache, refreshed in background); gallery + flight-data manifests network-first with 3 s timeout. Porkchop grids excluded from precache (browser HTTP cache only — they're large + per-route).
- Update toast: when a new SW is waiting, a small REFRESH toast appears at the bottom of the screen so users explicitly opt into the new version.
- Install-prompt deferral: the `beforeinstallprompt` event is held until the user has visited 3+ unique screens. Visit counter held in runtime memory only (per CLAUDE.md no-localStorage rule). Avoids the intrusive first-paint install banner.
- iOS: Safari ignores `beforeinstallprompt`. iOS users get manual install via the share sheet — same as before, now with proper SW-backed offline behaviour once installed.
ADR-029 documents the decision.
Manual high-contrast toggle
A small Aa button in the right side of the nav bar toggles `data-high-contrast="true"` on ``. The CSS hooks (already shipped in v0.1.10's `tokens.css`) bump dim/faint text alphas, thicken borders, and raise panel/HUD/nav backgrounds toward fully-opaque.
- Both OS preference (`prefers-contrast: more`) and the manual override are recognised — manual takes precedence.
- New `src/lib/high-contrast.ts` mirrors the existing `reduced-motion.ts` pattern: `matchMedia` + `MutationObserver` subscription, SSR-safe, cleanup function returned.
- 44 px touch target, `aria-pressed`, localised `aria-label`.
- Active state lit in teal; default outlined in faint grey.
Tests
4 new e2e tests in `tests/e2e/pwa.spec.ts`:
- `manifest.webmanifest` served + parseable
- `/sw.js` served + Workbox-generated
- `<link rel="manifest">` present in HTML head
- High-contrast toggle button flips `data-high-contrast` on ``
State: 207 unit + 77 e2e (1 skipped), all green.
Theme C status
- ✅ C1 — service worker (this release)
- ✅ C2 — manual high-contrast toggle (this release)
- ⏳ C3 — Lighthouse CI gate (deferred; needs separate `lhci` config)
Next phase
Phase 3 (v0.1.13) is re-using the new flight data on /fly + /missions: CAPCOM ticker reads `mission.flight.events[]`, NEXT EVENT row in the FLIGHT PARAMS HUD, data-quality badge on /missions cards.