Skip to content

Instantly share code, notes, and snippets.

@sengiv
Created April 12, 2026 04:37
Show Gist options
  • Select an option

  • Save sengiv/80a3dab537e22f2c5d79e08107bb40ed to your computer and use it in GitHub Desktop.

Select an option

Save sengiv/80a3dab537e22f2c5d79e08107bb40ed to your computer and use it in GitHub Desktop.
Full‑Stack PWA Implementation Blueprint for a Static‑Hosted HTML/JavaScript Astrology App

Full‑Stack PWA Implementation Blueprint for a Static‑Hosted HTML/JavaScript Astrology App

Goal, assumptions, and what “full compatibility” realistically means

This document is written as a technical instruction set for an AI coding agent that has access to an existing HTML/JavaScript astrology web app hosted on static hosting (for example, a static storage website). The goal is to convert the site into a production‑grade Progressive Web App (PWA) “with all the bells and whistles” where the platform allows it. citeturn1search24turn1search29turn0search12

Key constraint: “Full compatibility with Android and iOS” does not mean “every native feature on both OSes.” It means:

  • Implement PWA capabilities using standards (manifest, service worker, modern Web APIs). citeturn1search29turn2search0turn5search5turn1search21
  • Use progressive enhancement: enable features when the runtime supports them; provide fallbacks when it doesn’t. citeturn0search13turn1search7turn6search2
  • Be honest about sandbox boundaries: many PWA APIs depend on secure contexts (HTTPS) and service workers, and some are limited/experimental or differ across browser engines. citeturn10search2turn10search27turn2search4turn11view0

iOS reality check (important for “full compatibility”):

  • On iOS/iPadOS, push notifications for web apps exist (Home Screen web apps) from iOS/iPadOS 16.4, but permission must be requested via direct user interaction (e.g., tapping “Enable notifications”). citeturn4view0
  • By Safari 26 / iOS 26 / iPadOS 26, any website a user adds to Home Screen opens as a web app by default unless the user disables “Open as Web App” during the add flow; this reduces “installability” friction, but does not remove the need for a manifest/service worker if you want deeper PWA functionality. citeturn4view1turn1search2

Architectural implication: because you’re on static hosting, the only unavoidable “non‑static” component for a fully featured PWA is push + reminders, since Web Push is designed for an application server to send pushes at any time. Therefore you will need a backend component (serverless is fine) for subscription management and scheduled deliveries. citeturn2search1turn2search0turn13search2


Baseline PWA packaging and install UX

Manifest: required, and the place to declare “bells and whistles”

A web app manifest is a JSON file used by browsers/OS to treat a site like an app, including install metadata (name, icons, start URL, display mode), plus advanced features like shortcuts, share target, protocol handlers, and more. citeturn0search12turn6search8turn6search4turn5search0turn3search0turn5search2

At minimum you should define:

  • start_url (launch URL) citeturn0search0
  • scope (navigation boundary) citeturn0search4
  • display (standalone app‑like mode) citeturn0search8turn1search1turn1search21
  • icons (multiple sizes) citeturn0search12turn4view0

Also strongly consider id (stable unique identity for the install, useful for multi‑installs and some OS behaviours). citeturn6search0turn4view0turn6search16

Template manifest (agent should adapt names/paths to project):

{
  "id": "/?app=xingstar",
  "name": "XingStar Astrology",
  "short_name": "XingStar",
  "description": "Astrology reports, reminders, and personalised insights.",
  "lang": "en-GB",

  "start_url": "/?source=pwa",
  "scope": "/",

  "display": "standalone",
  "background_color": "#0B1020",
  "theme_color": "#0B1020",

  "icons": [
    { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
    { "src": "/icons/maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
  ],

  "shortcuts": [
    {
      "name": "Today’s horoscope",
      "short_name": "Today",
      "description": "Open today’s horoscope",
      "url": "/?route=today",
      "icons": [{ "src": "/icons/shortcut-today.png", "sizes": "96x96", "type": "image/png" }]
    },
    {
      "name": "Reminders",
      "short_name": "Reminders",
      "description": "Manage reminder times",
      "url": "/?route=reminders",
      "icons": [{ "src": "/icons/shortcut-reminders.png", "sizes": "96x96", "type": "image/png" }]
    }
  ],

  "share_target": {
    "action": "/share-handler",
    "method": "GET",
    "enctype": "application/x-www-form-urlencoded",
    "params": {
      "title": "title",
      "text": "text",
      "url": "url"
    }
  }
}

This uses manifest members that are defined and documented in modern PWA references, including start_url, scope, display, shortcuts, and share_target. citeturn0search0turn0search4turn0search8turn5search0turn3search0

iOS install experience: user‑driven “Add to Home Screen”

On iPhone/iPad, installation is typically done via Safari’s Share sheet → Add to Home Screen, and (on newer iOS) the user can toggle “Open as Web App.” citeturn1search2turn4view1

Agent instruction: implement an iOS install helper UI (non‑intrusive banner or modal) that appears only when:

  • The app is running in a browser tab (not standalone mode), and
  • The device appears to be iOS/iPadOS Safari/WebKit (feature detection first; UA detection only as a last resort), and
  • The user has visited at least N times or reached a “moment of value” (after generating first report).

This is aligned with best practices: when beforeinstallprompt isn’t supported, you can only show instructions for manual install, and you should only do it when in browser mode. citeturn0search13turn6search3turn6search2

Android/Chromium install experience: optional custom install button

On Chromium browsers, the beforeinstallprompt event can allow a customised install CTA that triggers the browser install prompt later via event.prompt(). However this event is explicitly described as non‑standard and not universally supported. citeturn0search5turn0search1turn0search13turn0search25

Agent instruction: implement a custom install button using safe feature detection and a fallback when unavailable.

Install prompt capture skeleton:

let deferredInstallPrompt = null;

window.addEventListener('beforeinstallprompt', (e) => {
  // Prevent automatic prompt
  e.preventDefault();
  deferredInstallPrompt = e;
  window.dispatchEvent(new CustomEvent('pwa:install-available'));
});

async function promptInstall() {
  if (!deferredInstallPrompt) return { outcome: 'unavailable' };
  deferredInstallPrompt.prompt();
  const choice = await deferredInstallPrompt.userChoice;
  deferredInstallPrompt = null;
  return choice; // { outcome: 'accepted'|'dismissed', platform: ... }
}

This matches the documented behaviour of beforeinstallprompt and prompt() being a developer‑triggered install prompt hook (where supported). citeturn0search5turn0search1turn0search9

Runtime mode detection: installed vs browser tab

To differentiate installed standalone mode vs browser tab:

  • Use display-mode media query via matchMedia. citeturn6search2turn6search3turn6search10

Mode detection snippet:

function getDisplayMode() {
  if (window.matchMedia('(display-mode: standalone)').matches) return 'standalone';
  if (window.matchMedia('(display-mode: fullscreen)').matches) return 'fullscreen';
  if (window.matchMedia('(display-mode: minimal-ui)').matches) return 'minimal-ui';
  return 'browser';
}

This is consistent with display-mode being intended for testing whether a web app is displayed as a tab vs standalone/fullscreen. citeturn6search2turn6search3turn6search10


Offline-first architecture: service worker, caching strategy, storage, and update control

Service worker fundamentals (what it enables)

Service workers act like a programmable proxy between app, browser, and network; they enable offline experiences, intercept requests, and provide access to push and background sync capabilities. citeturn2search0turn2search4turn2search32

They are available only in secure contexts (HTTPS, with localhost treated specially for development). citeturn10search27turn10search2turn1search24

Registration + scope rules (common “static hosting” pitfalls)

The service worker must be registered from the same origin and its scope is tied to its location unless configured (and may require special headers to increase scope). citeturn10search0turn10search12turn10search16

Registration snippet:

async function registerServiceWorker() {
  if (!('serviceWorker' in navigator)) return null;
  const reg = await navigator.serviceWorker.register('/sw.js', { scope: '/' });
  return reg;
}

register() associates the service worker script URL with a scope used for navigation/request matching. citeturn10search0turn10search15

Replacing “toy caching” with Workbox, and why injectManifest is usually correct

Workbox provides standard caching patterns (cache-first, network-first, stale-while-revalidate, etc.). citeturn2search2turn2search10turn2search7turn2search34

For a real app that also needs Web Push and other complex logic, Workbox recommends injectManifest (you write the SW, Workbox injects precache manifest). This is explicitly called out: generateSW is not for service workers that include additional Service Worker features like Web Push. citeturn2search3turn2search37

Recommended caching model for an astrology app (agent guidance)

Astrology apps usually have:

  • A mostly static shell (HTML/CSS/JS, icons, fonts)
  • API-driven personalised report data (user-specific)
  • “Evergreen” content (help pages, zodiac descriptions)
  • Potentially large generated artefacts (PDF reports, charts/images)

So implement a split strategy:

  1. Precache the app shell + offline page
  2. Navigation requests (mode: navigate) → network-first with offline fallback
  3. API requests → network-first with short fallback to cache + stale warning
  4. Images & static media → cache-first with expiration limits
  5. User-specific reports → store in IndexedDB as the true offline store (not only Cache Storage)

This aligns with service workers being used to build offline-first experiences and with caching strategy choice being app-dependent. citeturn2search4turn2search24turn3search2

Service worker skeleton (Workbox injectManifest style)

/* global workbox */
import { clientsClaim } from 'workbox-core';
import { precacheAndRoute, cleanupOutdatedCaches } from 'workbox-precaching';
import { registerRoute, setCatchHandler } from 'workbox-routing';
import { NetworkFirst, StaleWhileRevalidate, CacheFirst } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';

self.skipWaiting();
clientsClaim();

cleanupOutdatedCaches();
precacheAndRoute(self.__WB_MANIFEST);

// HTML navigations: network-first + offline fallback
registerRoute(
  ({ request }) => request.mode === 'navigate',
  new NetworkFirst({ cacheName: 'html' })
);

// API: network-first with short timeout
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new NetworkFirst({
    cacheName: 'api',
    networkTimeoutSeconds: 3,
    plugins: [new ExpirationPlugin({ maxEntries: 200, maxAgeSeconds: 60 * 10 })]
  })
);

// Static images: cache-first + expiration
registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({
    cacheName: 'images',
    plugins: [new ExpirationPlugin({ maxEntries: 400, maxAgeSeconds: 60 * 60 * 24 * 30 })]
  })
);

// “Content” pages: stale-while-revalidate
registerRoute(
  ({ url }) => url.pathname.startsWith('/content/'),
  new StaleWhileRevalidate({ cacheName: 'content' })
);

// Offline fallback handling
setCatchHandler(async ({ event }) => {
  if (event.request.mode === 'navigate') {
    return caches.match('/offline.html');
  }
  return Response.error();
});

This approach uses documented Workbox strategies (including stale-while-revalidate as a standard runtime caching pattern). citeturn2search2turn2search6turn2search3turn2search4

Storage durability and “iOS offline gotchas”

Offline support is not just caching; it requires storage management (Cache Storage + IndexedDB + StorageManager). citeturn8search10turn8search2turn8search1turn2search12

Implement:

  • Cache Storage for assets/responses (fast, but can be evicted). citeturn2search12
  • IndexedDB for user data and report content (structured, persistent offline store). citeturn8search1turn8search5
  • Persistent storage request to reduce eviction risk where supported (navigator.storage.persist()). citeturn8search0turn8search2turn8search23

Persistent storage request:

async function tryPersistStorage() {
  if (!navigator.storage?.persist) return false;
  return await navigator.storage.persist();
}

Persistent buckets are designed not to be cleared automatically under storage pressure without user consent, while best-effort storage may be evicted. citeturn8search21turn8search23turn8search0

Safari/WebKit storage: quota/enforcement varies by version and context. MDN documents that Safari/WebKit quota rules differ for browser apps vs embedded web content, and that saved Home Screen web apps use the “browser app” origin quota (modern Safari versions). citeturn8search15
Historically, WebKit documented a fixed Cache API quota of 50 MiB per partition in a WebKit engineering post; treat this as historical context and still test on target iOS versions. citeturn8search7

Update strategy (avoid “PWA stuck on old version”)

Agent should implement:

  • A “new version available” UX (banner/modal) when a new service worker is waiting, and a “Reload to update” action.
  • Ensure the SW script and precache manifest aren’t cached incorrectly by the HTTP cache.

Service workers update behaviour, lifecycle control, and the fact they can be terminated/restarted is fundamental; state must be persisted in storage (IndexedDB/Cache) rather than memory. citeturn2search32turn2search4turn2search0


Engagement features: reminders, push notifications, badges, background sync

Web Push + Notifications: the only cross‑platform “true reminders” mechanism

The Push API + Notifications API + service worker is the standards-based stack for push notifications. citeturn2search1turn2search13turn7search35turn2search0

The Push API is explicitly designed so an application server can send a push message at any time, even if the user agent or web app is inactive; delivery goes through a push service and wakes the service worker. citeturn13search2turn2search1turn2search5

iOS specifics (must implement exactly like this):

  • Web Push support exists for Home Screen web apps; permission requests must be initiated by direct user interaction. citeturn4view0
  • Web Push on iOS uses Apple Push Notification service (APNs), and WebKit advises allowing *.push.apple.com in server endpoint allowlists/firewalls as needed; no Apple Developer Program membership is required. citeturn4view0
  • Home Screen web apps on iOS 16.4 also support the Badging API, including updates while handling push events. citeturn4view0turn3search1

(These constraints are essential if your “reminders” are a core product feature.)

End-to-end push architecture (agent deliverables)

Push requires:

  1. Browser registers SW
  2. User grants notification permission (UI/gesture)
  3. Client subscribes to PushManager using VAPID/application server key
  4. Client sends subscription to backend
  5. Backend stores subscriptions and sends pushes via Web Push Protocol with VAPID authentication
  6. SW handles push event and shows notifications (and optionally sets badge) citeturn2search1turn9search5turn9search2turn2search5turn7search36turn3search1turn4view0

Client-side subscription code (browser)

async function ensurePushSubscription({ vapidPublicKey }) {
  if (!('serviceWorker' in navigator)) throw new Error('no-sw');
  if (!('PushManager' in window)) throw new Error('no-push');
  if (!('Notification' in window)) throw new Error('no-notification');

  const permission = await Notification.requestPermission();
  if (permission !== 'granted') return null;

  const reg = await navigator.serviceWorker.ready;

  // Convert base64url VAPID key to Uint8Array
  const key = base64UrlToUint8Array(vapidPublicKey);

  // userVisibleOnly is required in some browsers like Chrome/Edge.
  const sub = await reg.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: key
  });

  return sub.toJSON();
}

function base64UrlToUint8Array(base64Url) {
  const padding = '='.repeat((4 - (base64Url.length % 4)) % 4);
  const base64 = (base64Url + padding).replace(/-/g, '+').replace(/_/g, '/');
  const raw = atob(base64);
  const out = new Uint8Array(raw.length);
  for (let i = 0; i < raw.length; i++) out[i] = raw.charCodeAt(i);
  return out;
}
  • PushManager.subscribe() returns a subscription containing endpoint + keys. citeturn2search1turn9search0turn9search4
  • userVisibleOnly: true is required in some browsers like Chrome and Edge (otherwise subscribe is rejected). citeturn13search1turn13search12
  • VAPID is defined by an IETF RFC and is used to identify/authenticate the application server to push services. citeturn9search2turn9search6turn9search5turn9search12

Service worker push handler

self.addEventListener('push', (event) => {
  let data = {};
  try { data = event.data ? event.data.json() : {}; } catch {}

  const title = data.title || 'XingStar';
  const options = {
    body: data.body || 'Your astrology update is ready.',
    icon: '/icons/icon-192.png',
    badge: '/icons/badge.png',
    data: data.data || {},
    tag: data.tag || 'xingstar-reminder',
    renotify: false
  };

  event.waitUntil((async () => {
    await self.registration.showNotification(title, options);

    // Optional: update app badge if supported
    if (self.navigator?.setAppBadge && typeof data.badgeCount === 'number') {
      await self.navigator.setAppBadge(data.badgeCount);
    }
  })());
});

self.addEventListener('notificationclick', (event) => {
  event.notification.close();
  const url = event.notification.data?.url || '/?route=notifications';
  event.waitUntil(clients.openWindow(url));
});
  • Push messages are delivered to the service worker push event. citeturn2search5turn2search9turn2search1
  • Notifications from SW are shown using ServiceWorkerRegistration.showNotification(). citeturn7search36turn2search1
  • Badging API uses setAppBadge()/clearAppBadge() (supported in iOS Home Screen web apps from iOS 16.4 per WebKit). citeturn3search1turn4view0turn3search9

Backend: VAPID + Web Push sending

For backend implementation, the agent can use any language; for speed, Node.js using a well-known library is often easiest. web.dev provides detailed guidance on the Web Push protocol and VAPID/application server keys. citeturn9search1turn9search5turn9search9

Node.js pseudo‑implementation sketch:

import webpush from 'web-push';

webpush.setVapidDetails(
  'mailto:support@yourdomain.example',
  process.env.VAPID_PUBLIC_KEY,
  process.env.VAPID_PRIVATE_KEY
);

// subscription is the JSON from client: { endpoint, keys: { p256dh, auth } }
async function sendPush(subscription, payloadObj) {
  const payload = JSON.stringify(payloadObj);
  await webpush.sendNotification(subscription, payload, {
    TTL: 60 * 60 // 1 hour
  });
}

VAPID is formally specified by the IETF, and web.dev describes how application server keys/VAPID are used to authenticate to push services. citeturn9search2turn9search1turn9search5turn9search12

Reminder scheduling strategy (cross‑platform)

If reminders must work on both Android and iOS: implement reminders as server‑scheduled Web Push:

  • User sets reminder time (and timezone) in app
  • Backend stores schedule
  • A scheduler/cron triggers pushes at the required time for each user
  • Service worker shows notification

This is aligned with the Push API model: application server can send push at any time. citeturn13search2turn2search1

Optional Android-only enhancement: Notification Triggers API can schedule local notifications without network, but it is an experimental capability and not the iOS-compatible primary plan. citeturn7search0turn7search16

Background Sync and Periodic Sync (use cautiously)

Background Sync lets the app defer tasks until connectivity is restored, implemented via service worker sync events; however MDN marks it as limited availability/experimental. citeturn11view0turn3search6turn3search14

Periodic Background Sync is also experimental and availability is limited; treat it as optional. citeturn3search3turn3search15turn3search11

Recommendation for production: implement a reliable offline queue using IndexedDB + “sync when online” in the foreground, and optionally register background sync when supported.


Advanced PWA feature inventory and how to apply it to an astrology app

This section enumerates “bells and whistles” beyond the home screen icon; each item includes why it helps and how to implement, with the assumption that availability varies by platform.

App shortcuts (icon long-press / context menu)

Use shortcuts to deep-link into core actions: today’s horoscope, generate report, reminders, saved reports. Shortcuts are defined in the manifest and are meant to improve productivity and feel more native. citeturn5search0turn5search17turn5search13

Web Share API and Share Target API (virality + inbound share)

  • Web Share API: let users share a horoscope/report link, text, and (where supported) files through OS share UI. citeturn3search31
  • Share Target: register your installed PWA as a target in the OS share sheet so other apps can send content into your PWA. citeturn3search0turn3search4turn3search8

For astrology, this can support:

  • Sharing “today’s horoscope” as a link card.
  • Inbound share: user shares an image or note into the app to attach to a journal entry (where file share targeting is supported). citeturn3search4turn3search8

Badging API (unread count on app icon)

Badges can represent unread reports/reminders and integrate with notification workflows. citeturn3search1turn4view0turn3search17

Launch Handler API (control multi-window and launch routing)

Launch handler allows controlling whether launches focus an existing app window or open a new one, via launch_handler in the manifest, with LaunchQueue available for custom handling. citeturn5search3turn5search9turn5search16turn5search12

Useful for astrology:

  • If user taps multiple notifications, focus the same app instance and route internally.

Protocol handlers (deep link schemes)

Protocol handlers allow the PWA to register to handle URL schemes (e.g., web+astrology://...) via manifest protocol_handlers. citeturn5search2turn5search23turn5search15

Use cases:

  • Marketing / QR codes that open the installed app to a report type or onboarding step.

File handling (mostly desktop Chromium; treat as optional)

An installed PWA may register file handlers via file_handlers, but MDN notes it is currently limited (Chromium-based, desktop OS). citeturn5search1turn5search14turn5search18turn5search26

Astrology use cases (desktop):

  • Open exported .json “birth chart profile” files or saved report PDFs (depending on your product design).

Wake Lock (keep screen awake during reading / meditation / guided flow)

Screen Wake Lock lets you request the device keep screen on while the app is visible (useful for long report reading or guided sessions). citeturn12search0turn12search4turn4view0

Screen orientation control (use carefully)

Orientation lock exists but has limited availability; implement only with feature detection and graceful fallback. citeturn12search1turn12search21turn12search33

Content Indexing API (offline discovery, Chrome-focused)

Content indexing lets you register offline-ready pages/content so the browser can surface them to users as available offline (Chrome has supported it). citeturn12search26turn12search2

Astrology mapping:

  • When a report is “saved offline,” add it to the content index (where supported) to help discovery.

Background Fetch API (large downloads; experimental)

Background Fetch supports managing long downloads in the background but is experimental/limited; use only if you truly have large assets. citeturn12search3turn12search7turn12search19turn12search31

Passkeys (WebAuthn) for “native-grade” login UX

Passkeys on the web use WebAuthn to provide strong authentication. citeturn7search1turn7search5turn7search13

Astrology mapping:

  • Reduce login friction for returning users and improve account security without native SDKs.

Payments (Payment Request / Apple Pay flow)

Payment Request API aims to simplify checkout flows; Safari supports Payment Request specifically for Apple Pay as documented by WebKit. citeturn7search2turn7search30turn7search6

Astrology mapping:

  • Subscription upgrades without forcing traditional checkout form UX (availability varies; implement fallback checkout). citeturn7search2turn7search34turn7search30

Security, privacy, and iOS/Android reliability rules that must be enforced

HTTPS is mandatory for serious PWA features

  • Installability requirements include serving over HTTPS (or localhost/loopback in dev). citeturn10search2turn1search24
  • Service workers require secure contexts; otherwise registration fails. citeturn10search27turn10search3turn2search0

Permission UX: especially for notifications

On iOS Home Screen web apps, requesting push permission must be tied to direct user interaction (a “subscribe” button), and permissions are managed per web app in Settings similarly to native apps. citeturn4view0
Agent must avoid auto-prompting on first load; instead request after a clear value moment (e.g., user enables reminders).

Storage management must be explicit

Offline experience should use:

  • Cache API for assets/responses. citeturn2search12
  • IndexedDB for structured offline data. citeturn8search1turn8search5
  • Persistent storage request where possible (navigator.storage.persist) to reduce eviction risk. citeturn8search0turn8search21turn8search23

Testing tooling

Use browser DevTools “Application” tooling to inspect manifest, service workers, caches, simulate offline/push, etc. citeturn10search9turn10search11


AI coding agent execution plan and acceptance criteria

Deliverables (files and subsystems the agent should create/modify)

  1. manifest.json (or .webmanifest) at site root, linked from HTML. citeturn0search12turn6search8
  2. Icon set: 192, 512, plus maskable variant recommended. citeturn0search12turn4view0
  3. sw.js built using Workbox injectManifest pattern (not generateSW if push/complex logic needed). citeturn2search3turn2search7
  4. offline.html fallback route and corresponding SW catch handler. citeturn2search4turn2search24
  5. Install UX:
    • Android/Chromium: custom install CTA gated by beforeinstallprompt support. citeturn0search5turn0search1turn0search13
    • iOS: “Add to Home Screen” guidance based on display-mode detection. citeturn1search2turn6search2turn0search13
  6. Push + reminders backend:
    • Endpoint to provide VAPID public key.
    • Endpoint to save/update subscriptions.
    • Scheduler to send reminder pushes. citeturn9search5turn9search2turn13search2
  7. Client feature flags and progressive enhancement wrappers for each advanced API (badging/share/wakelock/etc.). citeturn3search1turn3search31turn12search0

Acceptance criteria (must pass)

  • Installable behaviour:

    • Android/Chromium shows install affordance; custom install button prompts when available. citeturn0search5turn0search1turn10search14
    • iOS install instructions match Apple’s documented Add to Home Screen flow. citeturn1search2turn4view1
  • Offline:

    • App shell loads offline after first successful online load; offline.html appears for navigations when offline and uncached. citeturn2search4turn2search24
  • Updates:

    • A new deployment does not trap users on old app code indefinitely; update is detectable and user can refresh into new version.
  • Push notifications:

    • Android: subscribe + receive push while app not open. citeturn13search2turn2search5
    • iOS: when installed as a Home Screen web app, permission request occurs only from user gesture and push is delivered similarly to native notifications. citeturn4view0
  • Reminders:

    • Reminder scheduling works cross-platform via server push scheduling (no reliance on experimental local scheduling). citeturn13search2turn7search0

Notes for teams using Flutter Web (optional, only if the agent detects a Flutter build pipeline)

If the codebase is Flutter Web rather than plain HTML/JS: Flutter’s generated service worker from flutter build web is deprecated, and the official guidance is to disable it (--pwa-strategy=none) and build your own service worker (or use Workbox). citeturn0search6turn0search14turn0search2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment