Skip to main content

pwa

Use when the user asks to make a site installable, turn it into a PWA, add a service worker, add a web app manifest, enable "add to home screen", or support offline. Also use when adding favicon/app icons, iOS home-screen support (apple-mobile-web-app meta tags), or verifying PWA installability.

インストールへ移動

ソース情報

リポジトリ
spinspire/pocketbase-sveltekit-starter
ソースの最終更新活動
2026年8月21日 19:18
検出された SKILL.md の言語
英語
スター
509
フォーク
76

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
pwa
description
Use when the user asks to make a site installable, turn it into a PWA, add a service worker, add a web app manifest, enable "add to home screen", or support offline. Also use when adding favicon/app icons, iOS home-screen support (apple-mobile-web-app meta tags), or verifying PWA installability.
license
MIT
authors
SpinSpire Team
# PWA Installability Make a web app installable and iOS-friendly with a minimal service worker. Default posture is **network-only** (no caching) unless the app needs offline — pick a strategy deliberately, never by default. ## When to Use - "Make it installable / a PWA" - "Add a service worker" / "add a web manifest" / "add to home screen" - "Works offline?" or "works on iPhone?" ## The Three Pillars 1. **HTTPS** — required for service workers (localhost exempt in dev). 2. **Web app manifest** — name, icons, start_url, display → makes it installable. 3. **Service worker** — background script; active SW + valid manifest unlocks the install prompt. ## Quick Reference | Asset | Where it goes | Notes | |-------|---------------|-------| | `manifest.webmanifest` | site root (`static/` for SvelteKit) | name, short_name, start_url, scope, display `standalone`, theme/background color, icons 192+512 + maskable | | `favicon-32/48/64.png` | root | browser tab icons; also `apple-touch-icon.png` 180px for iOS | | `icon-192.png`, `icon-512.png` | root | required by Chrome for install | | `icon-maskable-512.png` | root | full-bleed background, content in center 80% safe zone | | `sw.js` | root (`static/` for SvelteKit) | register from client code; served at site root for correct scope | | `offline.html` (optional) | root | fallback for failed navigations if offline support is wanted | ## Service Worker ### Default: network-only (no caching) ```js self.addEventListener('install', () => { self.skipWaiting(); }); self.addEventListener('activate', (event) => { event.waitUntil(clients.claim()); }); self.addEventListener('fetch', (event) => { event.respondWith(fetch(event.request)); }); ``` This makes the app installable and always-fresh. Use for apps where data lives server-side / in a spreadsheet and serving stale data is wrong. ### Offline fallback (add only if offline support is requested) ```js const CACHE = 'capstone-v1'; self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE).then((c) => c.addAll(['/', '/offline.html', '/manifest.webmanifest', '/icon-192.png', '/icon-512.png'])) ); self.skipWaiting(); }); self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))).then(() => clients.claim()) ); }); self.addEventListener('fetch', (event) => { const url = new URL(event.request.url); if (event.request.mode === 'navigate') { event.respondWith(fetch(event.request).catch(() => caches.match('/offline.html'))); return; } if (url.origin === location.origin && ['css', 'js', 'svg', 'png', 'jpg', 'woff2'].some((ext) => url.pathname.endsWith('.' + ext))) { event.respondWith(caches.match(event.request).then((cached) => cached || fetch(event.request).then((res) => { const clone = res.clone(); caches.open(CACHE).then((c) => c.put(event.request, clone)); return res; }))); return; } event.respondWith(fetch(event.request)); }); ``` ## Caching strategy decision table | Strategy | Behavior | Use for | |----------|----------|---------| | **Network only** | Always hit network, never cache | Auth, live/volatile data, spreadsheets, anything where stale is wrong. **Default.** | | **Cache first** | Serve from cache, update in background | Static assets: css/js, fonts, images | | **Network first** | Try network, fall back to cache | HTML pages, API reads where offline read > error | | **Stale while revalidate** | Serve stale instantly, refresh cache | Semi-static content, lists | When caching, version the cache name and delete old caches in `activate`. Never cache `POST` or auth'd API responses. ## Registration (client side) Register on mount, not at parse time, so it never delays first paint: ```js if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js').catch((err) => console.warn('SW registration failed', err)); } ``` In SvelteKit, put this in `+layout.svelte` `onMount`. The SW must be at the site root (`/sw.js`) — `static/sw.js` ships there via adapter-static. ## Manifest ```json { "name": "Capstone — Construction business operations", "short_name": "Capstone", "description": "One-line description.", "start_url": "/", "scope": "/", "display": "standalone", "background_color": "#faf9f6", "theme_color": "#1f2933", "icons": [ { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }, { "src": "/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" } ] } ``` Link it in the HTML head: `<link rel="manifest" href="/manifest.webmanifest" />`. ## Icons from source images - Source should be square (e.g. 768×768). Crop/resize with ImageMagick: ```bash magick src.png -resize 192x192 icon-192.png magick src.png -resize 512x512 icon-512.png magick -size 512x512 xc:"#1f2933" \( src.png -resize 60% +repage \) -gravity center -composite icon-maskable-512.png ``` - Maskable icons need a full-bleed background with the logo at ~60% inside the center safe zone. ## iOS support (Safari / Add to Home Screen) iOS ignores `theme_color` and needs legacy meta tags: ```html <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <meta name="apple-mobile-web-app-title" content="AppName" /> <meta name="theme-color" content="#1f2933" /> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" /> ``` Caveats: - iOS has no `beforeinstallprompt` — users install via **Share → Add to Home Screen**. - iOS may evict service workers after ~7 days of inactivity (Intelligent Tracking Prevention) — expect re-registration on next visit. ## Pre-ship audit 1. `bun run check` (or project typecheck) and build pass. 2. Start dev server, then verify with agent-browser: - manifest + SW reachable: `curl -s -o /dev/null -w "%{http_code}" localhost:5173/manifest.webmanifest` → 200 - all icon assets return 200 (favicon-32/48/64, apple-touch-icon, icon-192/512, icon-maskable-512, sw.js) - SW registered + active: ``` agent-browser eval "navigator.serviceWorker.getRegistration().then(r => ({ scope: r?.scope, active: r?.active?.state }))" ``` - links present in head: `rel=icon`, `rel=apple-touch-icon`, `rel=manifest`, `meta[name=theme-color]` 3. Production requires HTTPS (Cloudflare Pages et al. provide it). Local dev on `http://localhost` is exempt for SW registration. 4. Optional: run Lighthouse PWA audit in Chrome DevTools. ## Common Mistakes - **Registering SW in `<head>` or at module top-level** — delays first paint; use onMount. - **Caching auth'd or volatile API responses** — serves stale or broken data. Keep those network-only. - **SW not at root** — `/sw.js` only controls the scope beneath it; a SW in `/js/sw.js` won't control `/`. - **Skipping maskable icon** — Android adaptive icons will crop the logo without one. - **Cache name never versioned / old caches never deleted** — disk fills and stale assets linger. - **Forgetting iOS meta tags** — app opens in Safari tab instead of standalone, wrong status bar.
GitHubで見る