# ADR-076: Mobile-First Viewport and Meta Tag Standard

## Status

Accepted, 2026-06-27.

## Status History

```yaml
status_history:
  - date: 2026-06-27
    status: Proposed
    changed_by: hkl
    reason: Formalising viewport meta tag standard for all PWAs targeting mobile-first field use
    changed_via: adr-kit (360lm)
  - date: 2026-06-27
    status: Accepted
    changed_by: hkl
    reason: Mobile-first is core platform design; field teams use Android phones; notch handling and scaling consistency required across all PWAs
    changed_via: adr-kit (360lm)
```

## Context

360lm PWAs are designed for field teams (recce agents, installation crews, activity reporters) who work exclusively on Android smartphones. These devices have varying screen sizes, pixel densities, notches, and Safe Area insets (rounded corners, notches, home indicators). Without a standard viewport meta tag configuration, users experience:
- Unwanted pinch-zoom (accessibility problem, UX friction)
- Notch clipping on content (content hidden behind screen hardware)
- Inconsistent rendering across PWAs and devices
- Inability to use Safe Area CSS values (`env(safe-area-inset-*)`) for notch handling

Each PWA is installed as a home-screen app (`display: standalone`), not a browser tab, so the meta tag configuration becomes critical — there is no browser URL bar to hint at Safe Area or provide manual viewport controls.

## Decision

Every 360lm PWA `<head>` MUST include the following viewport and capability meta tags:

```html
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="theme-color" content="<PWA primary colour>">
```

**Key requirements:**

1. **`viewport-fit=cover`** — Enables Safe Area CSS values (`env(safe-area-inset-left)`, `env(safe-area-inset-right)`, `env(safe-area-inset-top)`, `env(safe-area-inset-bottom)`) used by ADR-002 (safe-bottom.css) and ADR-081 (safe-area padding) to handle notches and rounded corners.

2. **`initial-scale=1` with NO `maximum-scale` or `user-scalable=no`** — Allows the browser's default viewport scaling (device-width mapping), and critically, allows user pinch-zoom for accessibility (users with vision loss, zooming to read labels). Never disable user zoom — this violates WCAG 2.1 Level AA accessibility standards.

3. **`theme-color` matching PWA primary colour** — Must match the primary colour defined in `manifest.json`. Android uses this value to tint the status bar and UI chrome, creating visual cohesion between the app and the device.

4. **`mobile-web-app-capable` and `apple-mobile-web-app-capable`** — Signals to iOS and Android that the PWA can be installed as a home-screen app. Both are required for consistent behaviour across platforms.

5. **`apple-mobile-web-app-status-bar-style: black-translucent`** — iOS Safari status bar styling. `black-translucent` allows content to extend behind the status bar while keeping text readable (black icon + translucent background). Pairs with `viewport-fit=cover` on iOS.

6. **`display: standalone` in manifest.json (NOT in meta tags)** — The standalone mode is declared in the web manifest, not as a meta tag.

**Decision Maker:** hkl

## Alternatives Considered

- **`viewport-fit=contain` (the default).** Rejected: Contains the viewport within the Safe Area, leaving unused screen space in notched corners. Field users see black letterbox bars on either side of a notch — wastes screen real estate. `viewport-fit=cover` is better for mobile-optimized designs.

- **`maximum-scale=1` to prevent zoom.** Rejected: Violates accessibility standards (WCAG 2.1 AA 1.4.4). Users with vision loss cannot magnify text or form labels. Also frustrates left-handed users and those with dexterity challenges who need to enlarge touch targets.

- **Inline Safe Area CSS in each PWA instead of using shared helpers.** Rejected: Creates code duplication; developers must remember to apply padding/margin to every sticky bottom element. A shared stylesheet (ADR-002) ensures consistency and makes future updates easier.

- **No viewport meta tag — rely on browser defaults.** Rejected: Browser defaults vary by device and browser version. Android Chrome and iOS Safari interpret missing viewport tags differently; some pinch-zoom by default (UX friction), some clip notches (content loss). Explicit declaration ensures consistent behaviour.

- **Disable user zoom entirely via `user-scalable=no`.** Rejected: Violates accessibility standards; users with vision loss or dexterity challenges rely on pinch-zoom. Not acceptable for a production platform.

## Consequences

**Positive:**
- Consistent viewport behaviour across all 360lm PWAs and all Android/iOS devices.
- Safe Area values enable notch-safe layouts (ADR-002, ADR-081); content never hidden behind screen hardware.
- User pinch-zoom works — accessibility compliant, no vision-impaired users blocked.
- Theme colour aligns app chrome with brand, improving perceived quality and professionalism.
- Home-screen installation is reliably supported on both Android and iOS.

**Negative / Trade-offs:**
- New PWA developers must know to include these tags; not automatically applied.
- If `viewport-fit=cover` is misused without Safe Area CSS (ADR-002 safe-bottom.css), content may clip behind notches — mitigated by mandatory safe-bottom.css inclusion (ADR-002) and code review.
- `theme-color` must match manifest.json primary colour; mismatch creates visual inconsistency — mitigated by explicit cross-reference in this ADR and developer checklists.

**Risks and mitigations:**
- Missing viewport meta tag causes rendering bugs on devices with notches — mitigated: all PWAs must include this tag; code review gates it.
- Removing `viewport-fit=cover` would break Safe Area CSS logic — mitigated: documented relationship to ADR-002 and ADR-081.
- User zoom disabled (if `maximum-scale=1` is ever added) blocks users with vision loss — mitigated: explicitly forbidden in this ADR; code review should flag any such additions.

## Related Decisions

- ADR-002 (safe-bottom.css) — Uses `env(safe-area-inset-bottom)` which requires `viewport-fit=cover` to activate.
- ADR-003 (mobile scroll) — Companion rule for mobile layout patterns.
- ADR-081 (safe-area padding for notches, when created) — Will use `env(safe-area-inset-*)` to extend padding around notches.

## References

- MDN Web Docs: [viewport meta tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Viewport_meta_tag)
- WCAG 2.1 Level AA: [1.4.4 Resize Text](https://www.w3.org/WAI/WCAG21/Understanding/resize-text.html) — requires user zoom to not be disabled
- Web.dev: [Notches and System UI Spacing](https://web.dev/viewport-fit-cover/)
- `memory/feedback_mobile_scroll.md` — original mobile layout rule capture
- `memory/feedback_safe_bottom.md` — Safe Area CSS rule capture
