☀️ Light

Build a PWA with AI — The Ibadah Tracker Guide

For vibe coders: copy one prompt, let AI generate all 3 files, deploy to InteractiveLink. That's it.

1 prompt → 3 files Offline-first Installable on phone Auto-updates No framework needed

What's in this guide

WTF is a PWA?

A Progressive Web App is a website that behaves like a real app on your phone. Here's what makes it different from a regular website:

Installs to home screenTap "Install" → icon on home screen, no app store
Works offlineOpen it with no internet, it still loads and works
No browser chromeLooks like a native app — no URL bar, no tabs
Auto-updatesPush a new file to server → user gets it automatically
TL;DR — A PWA makes your HTML page feel like a real app people download. Except it's just 3 files on a server.

What InteractiveLink Gives You Now

New: Each custom domain on InteractiveLink now gets 1 service worker (sw.js) and 1 manifest (manifest.json).

Before vs Now

Before ❌

  • You could upload HTML files
  • "Install" button just made a shortcut (a bookmark)
  • No offline support
  • No auto-updates — user had to manually reload
  • Still showed browser URL bar

Now ✅

  • Upload HTML + sw.js + manifest.json
  • "Install" gives a real PWA (standalone app, home screen icon)
  • Full offline support — opens without internet
  • Auto-updates when you push new files
  • No URL bar, looks like native app
Limit: 1 sw.js and 1 manifest.json per custom domain. If you want multiple PWA apps, use different domains or subdomains.

📄 yourapp.html

Your entire app. CSS, HTML, JavaScript — all in one file. This is what users see and interact with.

Name it whatever you want. We used ibadah.html.

⚙️ sw.js

Service Worker. This is the "magic" file that makes your app work offline and auto-update. It caches your HTML so it loads even without internet.

Must be named sw.js and live at domain root.

📋 manifest.json

Tells the browser "this is an app". Sets the app name, icon, colors, and makes the Install button appear.

Must be named manifest.json and live at domain root.

The AI Prompt — Copy & Paste This

Open your AI coding tool (GitHub Copilot Agent, Claude, ChatGPT, Cursor, etc.) and paste this prompt. It will generate all 3 files for you.

This is the actual prompt we used to build Ibadah Tracker (cleaned up). Modify the app name and features to match your idea.

Help me create a PWA app. Here are the requirements: App concept: - App name: Ibadah Tracker (change this to your app name) - It tracks daily 5-time prayer, Quran reading, fasting, and sadaqah - Add a hero section that shows a motivational Islamic quote and the user's daily stats (prayers done, ayat read, etc.) - Track statistically interesting data (streaks, totals, averages) to show the user later Architecture (IMPORTANT — follow exactly): - Offline-first PWA that behaves like a real installed app - Only 3 files: one HTML file (e.g. ibadah.html), sw.js, manifest.json - The HTML file contains ALL CSS and JavaScript inline — no external files - No frameworks, no build tools, no npm - Use localStorage for all data storage (prefix all keys with "ibadah_") - Use inline SVG data URIs for manifest icons — no image files Service Worker (sw.js): - Cache the app shell on install: './', './ibadah.html', './manifest.json' - Network-first strategy for HTML and manifest.json (so users get fresh versions when online) - Cache-first strategy for other assets (speed) - Handle SKIP_WAITING message from the app (so app can control when to activate updates) - Handle GET_VERSION and CLEAR_CACHE messages - Use versioned cache names like 'myapp-v1.0.0' and clean old caches on activate - Do NOT auto-call skipWaiting() on install — let the app decide when manifest.json: - display: standalone (no browser chrome) - Use inline SVG for all icons (192x192 and 512x512 with purpose "any" + 512x512 maskable) - Add app shortcuts for quick actions - orientation: portrait-primary Install UX: - For Chrome/Edge: capture beforeinstallprompt, show custom install banner with dismiss option - For Safari iOS: detect iOS via user agent, show instructions "Tap Share → Add to Home Screen" - For Safari macOS: detect Mac Safari, show instructions "File → Add to Dock" - Detect standalone mode to hide install banners if already installed - Store dismiss state in localStorage so banners don't keep showing Update system: - Add APP_VERSION constant in the HTML file - On startup: register service worker, call .update() immediately - Listen for 'updatefound' event → show "Update Available" banner when new SW is waiting - On visibility change: check for SW updates again - Periodic check: every 5 minutes call serviceWorkerRegistration.update() - Settings page with manual "Check for Updates" button that: - Fetches all 3 files with cache:'no-store' and timestamp cache-busting - Computes SHA-256 hash of each file - Compares against stored hashes in localStorage - Also regex-extracts APP_VERSION from fetched HTML and compares to running version - Shows which files changed - "Apply Update" button: sends SKIP_WAITING to SW, clears all caches, hard reloads - Listen for 'controllerchange' on navigator.serviceWorker → reload page Settings page should also have: - Current version display - Service Worker status - Cache size info - Clear cache button - Force refresh button - Export data as JSON backup - Reset all data (with double confirmation) Important for the HTML file: - Use event delegation for any list that re-renders (e.g. prayer checkboxes) — attach click listener to the PARENT container, not individual items. This prevents the bug where you have to refresh after checking an item. Placeholder for future: - Online sync / user accounts (implement the UI placeholder, not the actual sync) Generate all 3 complete files.
That's it. Paste this prompt, AI generates the 3 files, upload them to your InteractiveLink domain. You have a real PWA.
Customize it: Replace "Ibadah Tracker" with your app name, change the features to match what you're building. The architecture and technical requirements stay the same — that's what makes it a proper PWA.

Bugs You'll Probably Hit (We Did)

AI-generated code works 90% of the time. Here are the issues we had to fix after getting the initial code:

🐛 Bug 1: Prayer checkboxes need page refresh

What happens: You check a prayer, but can't check the next one without refreshing the page.

Why: The code attaches click listeners directly to each checkbox element. When the list re-renders (updates the HTML), those elements are destroyed and the listeners are gone.

Fix: Use event delegation — attach the listener to the parent container instead.

// ❌ BAD — listeners die on re-render
document.querySelectorAll('.prayer-check').forEach(el => {
    el.addEventListener('click', () => toggle(el.dataset.prayer));
});

// ✅ GOOD — event delegation, survives re-render
document.getElementById('prayerList').addEventListener('click', (e) => {
    const check = e.target.closest('.prayer-check');
    if (check) toggle(check.dataset.prayer);
});
Tell the AI explicitly: "Use event delegation for any list that re-renders." We included this in the prompt above.
🐛 Bug 2: No install button on Safari

What happens: Users on iPhone/Safari don't see any way to install the app.

Why: Safari doesn't support beforeinstallprompt. That's Chrome/Edge only.

Fix: Detect Safari/iOS and show manual install instructions instead of a button.

// Detect iOS
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent)
    || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);

// Show: "Tap Share → Add to Home Screen → Add"
🐛 Bug 3: Updates only check the HTML file

What happens: You update sw.js or manifest.json but the app says "no updates available."

Why: Initial code may only check ibadah.html for changes, forgetting the other 2 files.

Fix: Tell AI to check all 3 files. Our update checker fetches ibadah.html, manifest.json, AND sw.js, hashes all three with SHA-256, and compares against stored hashes.

Pro tip: When you find a bug, tell the AI exactly what's wrong and ask it to fix it. Example: "The prayer checkboxes require a page refresh between checks. Fix using event delegation on the parent prayerList container."

How the App Actually Works

You don't need to understand all of this to use the prompt. But here's the quick version if you're curious.

The flow (5 steps)

1 User opens your page — browser loads ibadah.html
2 HTML registers sw.js — the browser downloads and installs the service worker
3 SW caches files — saves ibadah.html and manifest.json locally on the device
4 Next visit (even offline) — SW serves the cached files. App loads localStorage for data.
5 You push an update — SW detects byte change in sw.js → app shows "Update Available" → user taps → reload with new version

What goes where

User datalocalStorage (prayers, stats, streaks) — survives cache clears
App filesSW cache (HTML, manifest) — makes it work offline
Install infomanifest.json — name, icon, standalone mode
Offline logicsw.js — network-first for HTML, cache-first for assets

How Updates Work

Two things happen in parallel. You don't need to do anything special — just upload new files.

Automatic (browser does this)

  • Browser re-checks sw.js periodically
  • If even 1 byte changed → new SW installs in background
  • App detects the waiting SW → shows "Update Available" banner
  • User taps "Update Now" → new SW activates → page reloads

Also checks every 5 minutes and when app becomes visible again.

Manual (Settings → Check for Updates)

  • Fetches all 3 files fresh (no cache)
  • Computes SHA-256 hash of each file
  • Compares against last known hash
  • Shows exactly which files changed
  • "Apply Update" → clears caches → hard reload

Hashes stored in localStorage: ibadah_ibadah_hash, ibadah_sw_hash, ibadah_manifest_hash

Deploy new version = upload updated files. Users get the new version next time the app checks, usually within minutes.

Quick Reference

Version tracking in our files

ibadah.html

const APP_VERSION = '1.0.0';

The only version string users see. Update checker reads this via regex + compares SHA-256 hash.

sw.js

const CACHE_NAME = 'ibadah-tracker-v1.0.0';
const APP_SHELL_CACHE = 'ibadah-shell-v1.0.0';

Version is hardcoded inside cache names. Update detected via SHA-256 hash (no parsing).

manifest.json

{ "name": "Ibadah Tracker", ... }

No version field at all. Update detected purely via SHA-256 hash.

localStorage keys we use

ibadah_stats               — all user data (prayers, streaks, quran sessions, etc.)
ibadah_install_dismissed   — Chrome install banner dismissed
ibadah_safari_install_dismissed  — Safari banner dismissed
ibadah_last_update_check   — timestamp of last manual check
ibadah_ibadah_hash         — SHA-256 hash of ibadah.html
ibadah_manifest_hash       — SHA-256 hash of manifest.json
ibadah_sw_hash             — SHA-256 hash of sw.js

When releasing a new version

  1. Change APP_VERSION in ibadah.html
  2. Change CACHE_NAME and APP_SHELL_CACHE in sw.js
  3. Upload all 3 files

Even if you forget to bump versions, the SHA-256 hash check catches any byte change. But bumping versions is good practice since the version number shows in Settings.