> Part of the PWA DevGuide (split from pwa_dev_style.md on 2026-07-02 — see that file for the index; ADR-099).

## 22. App Update Banner Pattern

Non-disruptive notification shown once per app version upgrade. Works with blob-registered service workers (where `updatefound` events are unreliable).

### 22.1 How It Works

```
On init:
  seen = localStorage.getItem('app-ver-seen')
  localStorage.setItem('app-ver-seen', CACHE_VER)
  if (seen && seen !== CACHE_VER) → show banner
  (no banner on first install, no banner on same-version reload)
```

### 22.2 CSS

```css
.upd-banner {
  position: fixed;
  bottom: calc(var(--bnav-h) + var(--host-bar) + 10px);
  left: 12px; right: 12px;
  background: #16a34a; color: #fff;
  padding: 11px 14px;
  border-radius: var(--r);
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  z-index: 550;
  box-shadow: 0 4px 16px rgba(0,0,0,.18);
  transform: translateY(120px); opacity: 0;
  transition: transform .35s ease, opacity .35s ease;
  pointer-events: none;
}
.upd-banner.show { transform: translateY(0); opacity: 1; pointer-events: auto }
```

### 22.3 HTML

```html
<div class="upd-banner" id="upd-banner">
  <span style="font-size:13px;font-weight:600">
    <span class="en-txt">✓ App updated to latest version</span>
    <span class="hi-txt">✓ ऐप नया संस्करण अपडेट हुआ</span>
  </span>
  <button class="upd-banner-ok" onclick="dismissUpdateBanner()">
    <span class="en-txt">OK</span><span class="hi-txt">ठीक है</span>
  </button>
</div>
```

### 22.4 JS

```js
const CACHE_VER = 'app-v6';   // increment on every release, same as SW cache name

function checkAppUpdate() {
  const seen = localStorage.getItem('recce-ver-seen');
  localStorage.setItem('recce-ver-seen', CACHE_VER);
  if (seen && seen !== CACHE_VER) {
    const b = document.getElementById('upd-banner');
    if (b) { b.classList.add('show'); setTimeout(() => b.classList.remove('show'), 5000); }
  }
}
function dismissUpdateBanner() {
  document.getElementById('upd-banner')?.classList.remove('show');
}
```

Call `checkAppUpdate()` early in `init()`, after `setLang()` / `applyFontSize()` and before `initDB()`.

### 22.5 Release Checklist

When shipping a new version of any PWA with this pattern:
1. Increment SW cache name: `'recce-v5'` → `'recce-v6'`
2. Increment `CACHE_VER` to match: `'recce-v6'`
3. Both must be the same string — the banner fires on mismatch between `CACHE_VER` and the stored seen value

---

