# ADR-031: Complex Forms Use Full-Screen Overlays — Modals Only for Simple Confirmations

## Status

Accepted, 2026-06-22.

## Status History

```yaml
status_history:
  - date: 2026-06-22
    status: Proposed
    changed_by: hkl
    reason: Formalising form-vs-modal decision from Sales and Custodian PWA UX iterations
    changed_via: adr-kit (360lm)
  - date: 2026-06-22
    status: Accepted
    changed_by: hkl
    reason: Full-screen form pattern in use across Sales, Custodian, HR, Recce, Vehicle PWAs
    changed_via: adr-kit (360lm)
```

## Context

Early PWA iterations used bottom-sheet modals for forms (e.g. "Add Transfer" in Custodian, "New Invoice" in Sales). On mobile, the virtual keyboard pushes the viewport up and partially covers modal content — fields near the bottom of a modal are hidden behind the keyboard and the modal cannot scroll correctly because it is positioned inside a scrolling parent. Additionally, multi-step forms and complex validation feedback (inline errors, dynamic field visibility) are cramped in a fixed-height modal. When forms were converted to full-screen overlays with `visualViewport` listener, keyboard-covering issues resolved completely and form complexity became unconstrained.

## Decision

Forms with >3 input fields, or any form requiring keyboard input that has validation feedback, use **full-screen overlays**:
- `position: fixed; inset: 0; z-index: 1000; overflow-y: auto` — fills the viewport.
- `visualViewport resize` listener adjusts `padding-bottom` so content is never hidden behind the keyboard.
- Back navigation: pressing the overlay's close button (or browser back on mobile) dismisses the form — state is preserved in memory until submit or explicit cancel.

**Modals / bottom-sheets** are reserved for:
- Simple confirmations: ≤2 actions (Confirm / Cancel), single sentence of text.
- Single-value pickers: date picker, status selector, role assignment — one interaction and close.

Modals must never contain more than one screen worth of content and must never require keyboard input beyond a single short text field.

**Decision Maker:** hkl

## Alternatives Considered

- **Modal for all forms.** Rejected: virtual keyboard covers modal content on mobile; modal scroll interacts badly with page scroll; tested and UX was poor across Android and iOS (Custodian v3 iteration feedback).
- **Separate page navigation (router push) for forms.** Rejected: single-HTML-file PWA architecture (ADR-013) uses no router; full page reload for every form would reset app state; overlay pattern achieves page-like feel within the SPA.
- **Resize modal when keyboard appears.** Rejected: `window.resize` events are unreliable across Android/iOS for keyboard detection; `visualViewport` API works correctly but the resize logic is complex and brittle inside a modal (must account for modal position + keyboard height + scroll offset); full-screen overlay with simple `padding-bottom` adjustment is far simpler.

## Consequences

**Positive:**
- No keyboard-coverage issues — full-screen overlay scrolls its own content independently.
- Form complexity is unconstrained — multi-step, dynamic field visibility, inline validation all work.
- Consistent back-navigation feel — closing the form behaves like browser back.
- One reusable pattern across all PWAs.

**Negative / Trade-offs:**
- Full-screen overlay "hides" the rest of the app — users cannot reference list data while filling a form (they must close and reopen).
- More DOM to manage (show/hide overlay, preserve state on cancel) than a simple `window.confirm()`.
- `z-index: 1000` must be reserved for forms; other overlays (toasts, type-ahead dropdowns) must use lower z-indices.

**Risks and mitigations:**
- Multiple overlays stacked (e.g. type-ahead inside a form): mitigated by keeping type-ahead dropdowns within the form overlay at `z-index: 1001` and constraining type-ahead to its form.
- State lost if overlay closed accidentally: mitigated by preserving form state in a JS object until explicit Cancel; prompt "Discard changes?" before closing if form is partially filled.

## Related Decisions

- ADR-013 (single HTML file, no framework) — full-screen overlay is the SPA navigation substitute.
- ADR-030 (no `<select>` for >5 options) — type-ahead inputs inside full-screen forms work without z-index conflicts.
- ADR-003 (mobile scroll root document) — full-screen overlay scrolls its own container, root document scroll paused while overlay is open.

## References

- `memory/dbt_sales.md` — form UX iterations note (modal → full-screen conversion)
- `memory/dbt_custodian.md` — transfer form full-screen implementation
- `pwa_dev_style.md` §12 — Full-Screen Form Pattern with visualViewport
