<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PWA Starter (Vite + Workbox)</title>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{
font-family:system-ui,-apple-system,sans-serif;
background:#0a0a0f;
color:#e2e8f0;
padding:24px;
line-height:1.6;
}
.card{
background:rgba(255,255,255,0.03);
border:1px solid rgba(255,255,255,0.08);
border-radius:12px;
padding:28px;
max-width:720px;
margin:0 auto;
}
h1{
font-size:1.5rem;
font-weight:700;
margin-bottom:4px;
color:#fff;
}
.subtitle{
color:#94a3b8;
font-size:0.875rem;
margin-bottom:24px;
}
.section-title{
font-size:0.75rem;
font-weight:600;
text-transform:uppercase;
letter-spacing:0.08em;
color:#22d3ee;
margin-bottom:12px;
margin-top:24px;
}
.cli{
font-family:'SF Mono',SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;
font-size:0.8rem;
background:rgba(34,211,238,0.08);
color:#22d3ee;
padding:8px 12px;
border-radius:6px;
margin-bottom:16px;
overflow-x:auto;
white-space:nowrap;
}
.code-block{
font-family:'SF Mono',SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;
font-size:0.75rem;
color:#94a3b8;
background:rgba(255,255,255,0.02);
border:1px solid rgba(255,255,255,0.06);
border-radius:8px;
padding:14px 18px;
line-height:1.65;
margin-bottom:16px;
overflow-x:auto;
white-space:pre;
}
.code-block .kw{color:#22d3ee}
.code-block .str{color:#3b82f6}
.code-block .comment{color:#64748b;font-style:italic}
.tree{
font-family:'SF Mono',SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;
font-size:0.78rem;
color:#94a3b8;
background:rgba(255,255,255,0.02);
border:1px solid rgba(255,255,255,0.06);
border-radius:8px;
padding:14px 18px;
line-height:1.7;
margin-bottom:16px;
}
.tree .folder{color:#3b82f6}
.tree .file{color:#e2e8f0}
.tree .comment{color:#64748b;font-style:italic}
.checklist{
list-style:none;
display:grid;
grid-template-columns:1fr 1fr;
gap:6px 16px;
margin-bottom:16px;
}
.checklist li{
font-size:0.82rem;
color:#e2e8f0;
}
.checklist li::before{
content:"\2713 ";
color:#22d3ee;
font-weight:700;
margin-right:4px;
}
.strategies{
display:grid;
grid-template-columns:1fr 1fr 1fr;
gap:10px;
margin-bottom:16px;
}
.strategy{
background:rgba(255,255,255,0.02);
border:1px solid rgba(255,255,255,0.06);
border-radius:8px;
padding:12px;
}
.strategy-name{
font-weight:600;
font-size:0.82rem;
color:#22d3ee;
margin-bottom:4px;
}
.strategy-desc{
font-size:0.72rem;
color:#94a3b8;
margin-bottom:4px;
}
.strategy-use{
font-size:0.7rem;
color:#64748b;
font-style:italic;
}
.links{
display:flex;
gap:12px;
flex-wrap:wrap;
margin-top:20px;
padding-top:16px;
border-top:1px solid rgba(255,255,255,0.06);
}
.links a{
font-size:0.78rem;
color:#3b82f6;
text-decoration:none;
padding:4px 10px;
border:1px solid rgba(59,130,246,0.3);
border-radius:6px;
transition:background 0.2s;
}
.links a:hover{background:rgba(59,130,246,0.1)}
</style>
</head>
<body>
<div class="card">
<h1>PWA Starter (Vite + Workbox)</h1>
<p class="subtitle">Progressive Web App — installable, offline-first, service worker powered.</p>
<div class="section-title">Install</div>
<div class="cli">npm i -D vite-plugin-pwa</div>
<div class="section-title">vite.config.ts</div>
<div class="code-block"><span class="kw">import</span> { VitePWA } <span class="kw">from</span> <span class="str">"vite-plugin-pwa"</span>;
<span class="kw">export default</span> defineConfig({
plugins: [
VitePWA({
registerType: <span class="str">"autoUpdate"</span>,
manifest: {
name: <span class="str">"My PWA App"</span>,
short_name: <span class="str">"MyPWA"</span>,
theme_color: <span class="str">"#0a0a0f"</span>,
icons: [
{ src: <span class="str">"/icons/192.png"</span>, sizes: <span class="str">"192x192"</span> },
{ src: <span class="str">"/icons/512.png"</span>, sizes: <span class="str">"512x512"</span> },
],
},
}),
],
});</div>
<div class="section-title">Folder Structure</div>
<div class="tree">
<span class="folder">public/</span><br/>
<span class="folder">icons/</span><br/>
<span class="file">192.png</span> <span class="comment"># App icon</span><br/>
<span class="file">512.png</span> <span class="comment"># Splash icon</span><br/>
<span class="file">robots.txt</span><br/>
<span class="folder">src/</span><br/>
<span class="file">main.ts</span><br/>
<span class="file">sw.ts</span> <span class="comment"># Custom service worker (optional)</span><br/>
<span class="file">vite.config.ts</span> <span class="comment"># VitePWA() config</span><br/>
</div>
<div class="section-title">PWA Checklist</div>
<ul class="checklist">
<li>Manifest</li>
<li>Service Worker</li>
<li>Offline Mode</li>
<li>Installable</li>
<li>HTTPS</li>
<li>Responsive</li>
</ul>
<div class="section-title">Caching Strategies</div>
<div class="strategies">
<div class="strategy">
<div class="strategy-name">CacheFirst</div>
<div class="strategy-desc">Serve from cache, fall back to network.</div>
<div class="strategy-use">Static assets, fonts, icons</div>
</div>
<div class="strategy">
<div class="strategy-name">NetworkFirst</div>
<div class="strategy-desc">Try network, fall back to cache.</div>
<div class="strategy-use">API responses, dynamic data</div>
</div>
<div class="strategy">
<div class="strategy-name">StaleWhileRevalidate</div>
<div class="strategy-desc">Serve cache, update in background.</div>
<div class="strategy-use">Semi-static content, avatars</div>
</div>
</div>
<div class="links">
<a href="https://vite-pwa-org.netlify.app/guide/" target="_blank" rel="noopener">vite-plugin-pwa Guide</a>
<a href="https://developer.chrome.com/docs/workbox" target="_blank" rel="noopener">Workbox Docs</a>
<a href="https://github.com/pwa-builder/pwa-starter" target="_blank" rel="noopener">pwa-builder/pwa-starter</a>
</div>
</div>
</body>
</html>