docs: add PWA design spec for apps/main
Covers installable PWA with vite-plugin-pwa, icon generation, custom install prompt, mobile polish, and deployment strategy. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
949888ec63
commit
61ec7e44cf
1 changed files with 180 additions and 0 deletions
180
docs/superpowers/specs/2026-04-02-pwa-design.md
Normal file
180
docs/superpowers/specs/2026-04-02-pwa-design.md
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
# PWA Design Spec: apps/main
|
||||
|
||||
**Date**: 2026-04-02
|
||||
**Goal**: Make apps/main installable as a native-feeling PWA with fast repeat loads, focused on mobile experience polish. No offline data, no push notifications.
|
||||
**Approach**: `vite-plugin-pwa` (Workbox-based) integrated into the existing Vite build.
|
||||
|
||||
---
|
||||
|
||||
## 1. Web App Manifest & Icons
|
||||
|
||||
### Manifest
|
||||
|
||||
Generated by `vite-plugin-pwa` from config in `vite.config.ts`. Key properties:
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| `name` | XTablo |
|
||||
| `short_name` | XTablo |
|
||||
| `start_url` | `/` |
|
||||
| `display` | `standalone` |
|
||||
| `theme_color` | Matched to app header (from Tailwind config) |
|
||||
| `background_color` | Matched to app background |
|
||||
| `orientation` | `any` |
|
||||
| `scope` | `/` |
|
||||
|
||||
### Icons
|
||||
|
||||
Generated from existing `icon.jpg` (production) and `staging_icon.jpg` (staging) using a one-time script (e.g., `sharp`). Output committed to `apps/main/public/pwa-icons/`.
|
||||
|
||||
Required sizes:
|
||||
- `16x16`, `32x32` — favicons
|
||||
- `180x180` — Apple touch icon
|
||||
- `192x192` — Android standard
|
||||
- `512x512` — Android standard
|
||||
- `512x512 maskable` — Android adaptive icon
|
||||
|
||||
### HTML Meta Tags
|
||||
|
||||
Added to `apps/main/index.html`:
|
||||
|
||||
```html
|
||||
<meta name="theme-color" content="#...">
|
||||
<link rel="apple-touch-icon" href="/pwa-icons/apple-touch-icon-180x180.png">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
```
|
||||
|
||||
Note: The manifest `<link>` is injected automatically by `vite-plugin-pwa`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Service Worker & Caching Strategy
|
||||
|
||||
### Plugin Configuration
|
||||
|
||||
`vite-plugin-pwa` in `vite.config.ts` with:
|
||||
- `registerType: 'autoUpdate'` — silent background updates, no user prompt
|
||||
- `workbox.globPatterns` — precache all build outputs (JS, CSS, fonts, images)
|
||||
|
||||
### Caching Rules
|
||||
|
||||
| Resource | Strategy | Notes |
|
||||
|----------|----------|-------|
|
||||
| App shell (HTML, JS, CSS) | Precache | Auto-generated by Workbox from build manifest |
|
||||
| Static assets (icons, logos) | Precache | Included in build output |
|
||||
| API calls (`/api/v1/*`) | Network only | Not cached; errors handled by React Query |
|
||||
| Supabase calls | Network only | Not cached; errors handled by React Query |
|
||||
|
||||
### SW Registration
|
||||
|
||||
In `main.tsx`, using `registerSW` from `virtual:pwa-register`. Handles registration lifecycle and auto-update.
|
||||
|
||||
### Update Flow
|
||||
|
||||
1. User opens/refreshes the app
|
||||
2. SW detects new precache manifest in background
|
||||
3. New assets downloaded silently
|
||||
4. New version activates on next navigation/refresh
|
||||
|
||||
No "update available" toast — the app always requires connectivity, so silent updates are sufficient.
|
||||
|
||||
---
|
||||
|
||||
## 3. Install Prompt UX
|
||||
|
||||
### Custom Install Prompt
|
||||
|
||||
A React hook `useInstallPrompt` that:
|
||||
- Listens for the `beforeinstallprompt` event
|
||||
- Stores the deferred prompt event
|
||||
- Exposes `canInstall: boolean` and `promptInstall(): void`
|
||||
|
||||
### UI
|
||||
|
||||
- Dismissible banner shown to authenticated users on the dashboard
|
||||
- Dismissal persisted in `localStorage` (key: `pwa-install-dismissed`)
|
||||
- Hidden when app is already running in standalone mode (detected via `window.matchMedia('(display-mode: standalone)')`)
|
||||
|
||||
### iOS Fallback
|
||||
|
||||
Safari does not support `beforeinstallprompt`. For iOS Safari users:
|
||||
- Detect via user agent
|
||||
- Show manual instruction: "Tap Share > Add to Home Screen"
|
||||
- Same dismissal logic applies
|
||||
|
||||
---
|
||||
|
||||
## 4. Mobile Polish & Standalone Experience
|
||||
|
||||
### Viewport & Safe Areas
|
||||
|
||||
Update viewport meta tag:
|
||||
```html
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
```
|
||||
|
||||
Add CSS safe area padding on the app shell:
|
||||
```css
|
||||
padding-top: env(safe-area-inset-top);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
padding-left: env(safe-area-inset-left);
|
||||
padding-right: env(safe-area-inset-right);
|
||||
```
|
||||
|
||||
### Splash Screens
|
||||
|
||||
Simple approach — no custom splash images. Use `theme_color` + `background_color` in the manifest for Android. iOS uses `apple-mobile-web-app-status-bar-style` for a clean launch.
|
||||
|
||||
### Status Bar
|
||||
|
||||
- Android: Controlled by `theme_color` in manifest
|
||||
- iOS: `apple-mobile-web-app-status-bar-style: default`
|
||||
|
||||
### Navigation
|
||||
|
||||
Standalone mode has no browser back button. The existing React Router navigation with in-app back buttons handles this. Verify no dead-end flows exist.
|
||||
|
||||
---
|
||||
|
||||
## 5. Environment & Deployment
|
||||
|
||||
### Environment Differences
|
||||
|
||||
| Aspect | Staging | Production |
|
||||
|--------|---------|------------|
|
||||
| Icon source | `staging_icon.jpg` | `icon.jpg` |
|
||||
| Manifest name | XTablo (Staging) | XTablo |
|
||||
| All other config | Same | Same |
|
||||
|
||||
### Cloudflare Workers
|
||||
|
||||
No changes to `wrangler.toml`. The SW file and manifest are static assets in `dist/`, served by Cloudflare like any other file. SPA routing (`not_found_handling = "single-page-application"`) is unaffected.
|
||||
|
||||
### Build Pipeline
|
||||
|
||||
- `vite-plugin-pwa` runs as part of normal `pnpm build` — no separate step
|
||||
- Icon generation is a one-time script, output committed to repo
|
||||
|
||||
### Local Testing
|
||||
|
||||
- `pnpm dev`: SW disabled (standard behavior for vite-plugin-pwa)
|
||||
- `pnpm build && pnpm preview`: Full PWA experience with active SW
|
||||
|
||||
### Impact
|
||||
|
||||
Fully additive. Users who don't install the PWA see no changes. The app works identically in a regular browser tab.
|
||||
|
||||
---
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Offline data access (all data requires connectivity)
|
||||
- Push notifications (deferred to future work)
|
||||
- Background sync
|
||||
- Custom splash screen images (using color-based approach instead)
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `vite-plugin-pwa` — Vite PWA integration
|
||||
- `sharp` (dev dependency) — icon generation script
|
||||
Loading…
Reference in a new issue