pwa-optimizer
Optimize @vite-pwa/astro configuration for PWA compliance and offline functionality
- Category: discovered
- Tags: github, community
- Version: 1.0.0
- Author: walid
- Downloads: 0
- Updated: 2025-11-22
Download the latest version (v1.0.0) from this page.
SKILL.md
---
name: pwa-optimizer
description: Optimize @vite-pwa/astro configuration for PWA compliance and offline functionality
activation:
keywords:
- pwa
- progressive web app
- service worker
- manifest.json
- offline
triggers:
- "vite-pwa"
- "workbox"
auto_load_project: cv-astro
---
# PWA Optimizer
## What This Is
Optimize Progressive Web App (PWA) configuration for CV_Astro using @vite-pwa/astro:
- manifest.json generation
- Service worker configuration
- Offline fallback pages
- Icon generation (512x512, 192x192, etc.)
- Cache strategy optimization
**For**: CV_Astro project (@vite-pwa/astro)
---
## When to Activate
Auto-activates when keywords detected:
- "pwa configuration"
- "service worker"
- "offline support"
- "manifest.json"
---
## PWA Configuration
### Astro Integration
**File**: `astro.config.mjs`
```javascript
import { defineConfig } from 'astro/config';
import { VitePWA } from '@vite-pwa/astro';
export default defineConfig({
integrations: [
VitePWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'masked-icon.svg'],
manifest: {
name: 'Oriol Macías - Portfolio',
short_name: 'Oriol M.',
description: 'Full Stack Developer specialized in modern web technologies',
theme_color: '#3b82f6',
background_color: '#ffffff',
display: 'standalone',
scope: '/',
start_url: '/',
orientation: 'portrait-primary',
icons: [
{
src: '/pwa-192x192.png',
sizes: '192x192',
type: 'image/png',
purpose: 'any'
},
{
src: '/pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any'
},
{
src: '/pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'maskable'
}
],
categories: ['business', 'productivity'],
lang: 'en',
dir: 'ltr'
},
workbox: {
globPatterns: ['**/*.{css,js,html,svg,png,jpg,jpeg,webp,woff,woff2}'],
runtimeCaching: [
{
urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
handler: 'CacheFirst',
options: {
cacheName: 'google-fonts-cache',
expiration: {
maxEntries: 10,
maxAgeSeconds: 60 * 60 * 24 * 365 // 1 year
},
cacheableResponse: {
statuses: [0, 200]
}
}
},
{
urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i,
handler: 'CacheFirst',
options: {
cacheName: 'gstatic-fonts-cache',
expiration: {
maxEntries: 10,
maxAgeSeconds: 60 * 60 * 24 * 365 // 1 year
},
cacheableResponse: {
statuses: [0, 200]
}
}
},
{
urlPattern: /\.(?:png|jpg|jpeg|svg|webp)$/,
handler: 'CacheFirst',
options: {
cacheName: 'images-cache',
expiration: {
maxEntries: 60,
maxAgeSeconds: 60 * 60 * 24 * 30 // 30 days
}
}
}
]
},
devOptions: {
enabled: true,
type: 'module'
}
})
]
});
```
---
## Manifest.json
### Essential Fields
```json
{
"name": "Oriol Macías - Full Stack Developer Portfolio",
"short_name": "Oriol M.",
"description": "Professional portfolio showcasing web development projects",
"theme_color": "#3b82f6",
"background_color": "#ffffff",
"display": "standalone",
"scope": "/",
"start_url": "/",
"orientation": "portrait-primary",
"icons": [
{
"src": "/pwa-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/apple-touch-icon.png",
"sizes": "180x180",
"type": "image/png"
}
],
"categories": ["business", "productivity", "utilities"],
"lang": "en",
"dir": "ltr",
"screenshots": [
{
"src": "/screenshots/desktop.png",
"sizes": "1280x720",
"type": "image/png",
"form_factor": "wide"
},
{
"src": "/screenshots/mobile.png",
"sizes": "750x1334",
"type": "image/png",
"form_factor": "narrow"
}
]
}
```
---
## Icon Generation
### Required Sizes
| Icon | Size | Purpose |
|------|------|---------|
| `pwa-192x192.png` | 192×192 | Android Chrome |
| `pwa-512x512.png` | 512×512 | Splash screen |
| `apple-touch-icon.png` | 180×180 | iOS |
| `favicon.ico` | 32×32 | Browser tab |
| `masked-icon.svg` | Vector | Safari pinned tab |
### Generation Script
**File**: `scripts/generate-pwa-icons.js`
```javascript
import sharp from 'sharp';
import fs from 'fs';
const sizes = [
{ name: 'pwa-192x192.png', size: 192 },
{ name: 'pwa-512x512.png', size: 512 },
{ name: 'apple-touch-icon.png', size: 180 },
{ name: 'favicon-32x32.png', size: 32 }
];
const source = 'src/assets/icon-source.png';
async function generateIcons() {
for (const { name, size } of sizes) {
await sharp(source)
.resize(size, size, { fit: 'cover' })
.toFile(`public/${name}`);
console.log(`✅ Generated ${name}`);
}
}
generateIcons();
```
**Run**:
```bash
node scripts/generate-pwa-icons.js
```
---
## Service Worker Strategies
### 1. CacheFirst (Static Assets)
**Use**: Fonts, images, CSS, JS
**Behavior**: Check cache first, fallback to network
```javascript
{
urlPattern: /\.(?:png|jpg|jpeg|svg|webp)$/,
handler: 'CacheFirst',
options: {
cacheName: 'images-cache',
expiration: {
maxEntries: 60,
maxAgeSeconds: 60 * 60 * 24 * 30 // 30 days
}
}
}
```
### 2. NetworkFirst (Dynamic Content)
**Use**: HTML pages, API responses
**Behavior**: Try network first, fallback to cache
```javascript
{
urlPattern: /^https:\/\/api\.example\.com\/.*/i,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
networkTimeoutSeconds: 10,
expiration: {
maxEntries: 50,
maxAgeSeconds: 60 * 60 * 24 // 1 day
}
}
}
```
### 3. StaleWhileRevalidate (Balance)
**Use**: Non-critical resources
**Behavior**: Return cache, update in background
```javascript
{
urlPattern: /\.(?:css|js)$/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'static-resources',
expiration: {
maxEntries: 60,
maxAgeSeconds: 60 * 60 * 24 * 7 // 7 days
}
}
}
```
---
## Offline Fallback
### Offline Page
**File**: `src/pages/offline.astro`
```astro
---
import Layout from '../layouts/Layout.astro';
import { t, changeLanguage } from 'i18next';
const locale = Astro.currentLocale || 'en';
changeLanguage(locale);
---
<Layout title={t('offline:title')} description={t('offline:description')}>
<div class="offline-container">
<h1>{t('offline:heading')}</h1>
<p>{t('offline:message')}</p>
<button onclick="window.location.reload()" class="retry-btn">
{t('offline:retry')}
</button>
</div>
</Layout>
<style>
.offline-container {
@apply flex flex-col items-center justify-center min-h-screen p-6 text-center;
}
h1 {
@apply text-4xl font-bold text-gray-900 mb-4;
}
p {
@apply text-xl text-gray-600 mb-8;
}
.retry-btn {
@apply bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors;
}
</style>
```
### Workbox Configuration
```javascript
workbox: {
navigationPreload: true,
offlineFallback: {
html: '/offline',
},
navigateFallback: '/offline',
navigateFallbackDenylist: [/^\/api\//]
}
```
---
## i18n Support in PWA
### Locale-Specific Manifests
```javascript
// astro.config.mjs
const getManifest = (locale) => ({
name: locale === 'es'
? 'Oriol Macías - Portafolio'
: 'Oriol Macías - Portfolio',
short_name: 'Oriol M.',
description: locale === 'es'
? 'Portafolio profesional de desarrollo web'
: 'Professional web development portfolio',
lang: locale,
dir: 'ltr'
});
```
---
## Testing PWA
### Chrome DevTools
1. **Open DevTools** → Application tab
2. **Manifest**: Check all fields valid
3. **Service Workers**: Verify registered
4. **Storage**: Check cache entries
5. **Offline**: Toggle offline mode, test
### Lighthouse PWA Audit
```bash
npm run build
npm run preview
# In another terminal
lighthouse http://localhost:4321 --only-categories=pwa --view
```
**Score Target**: ≥90/100
---
## PWA Checklist
### ✅ Required
- [ ] manifest.json with all required fields
- [ ] Icons (192×192, 512×512)
- [ ] Service worker registered
- [ ] HTTPS enabled (production)
- [ ] theme_color and background_color set
- [ ] display: standalone or minimal-ui
- [ ] start_url configured
- [ ] Offline fallback page
### ✅ Recommended
- [ ] Apple touch icon (180×180)
- [ ] Maskable icon (512×512)
- [ ] Screenshots for app stores
- [ ] Cache strategies optimized
- [ ] Offline page with i18n
- [ ] Update notification UI
### ✅ Optional
- [ ] Push notifications
- [ ] Background sync
- [ ] Periodic background sync
- [ ] File handling
- [ ] Share target
---
## Deployment
### Headers Configuration
**File**: `public/_headers`
```
/*
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
/manifest.json
Content-Type: application/manifest+json
Cache-Control: public, max-age=0, must-revalidate
/sw.js
Content-Type: application/javascript
Cache-Control: public, max-age=0, must-revalidate
/offline
Cache-Control: public, max-age=0, must-revalidate
```
---
## Quick Reference
| Feature | Implementation | Priority |
|---------|----------------|----------|
| **Manifest** | VitePWA config | CRITICAL |
| **Icons** | 192×192, 512×512 | CRITICAL |
| **Service Worker** | Auto-generated | CRITICAL |
| **Offline page** | `/offline` route | HIGH |
| **Cache strategies** | Workbox config | HIGH |
| **Update UI** | Toast notification | MEDIUM |
**Target**: Lighthouse PWA score ≥90/100