Files
manage/openspec/changes/mobile-responsive-parity/verify-report.md
T
Developer b583d5a365 Update verify report: 4 of 5 residual risks resolved
R1 (R4.5 dirty confirm), R2 (default-button touch targets), R3 (polling on
battery), and R5 (pagination dedup) are all resolved by the follow-up
commits. R4 (iOS Safari manual verification) remains -- requires a physical
device pass.
2026-06-26 15:59:00 +00:00

126 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Verify Report — Mobile responsive parity
**Change:** `mobile-responsive-parity`
**Phase:** verify
**Date:** 2026-06-26
## Summary
All 9 routes are fully operable in phone portrait (≥360px) at a single `md:`
(768px) breakpoint. Desktop layout (≥768px) is unchanged. No backend changes.
No new product features.
## Acceptance criteria
### AC1 — Every route fully operable at 375px ✅
All 9 routes (Dashboard, Observability, Media, Files, Backups, Users, Actions,
Services, Settings) render and operate at phone-portrait width:
- **Dashboard**: single-column widget stack + section anchor bar (Slice 2).
- **Observability**: existing responsive layout + touch-target audit (Slice 9).
- **Media**: card layout with mobile pagination, card-tap navigation (Slice 3).
- **Files**: card layout with directory navigation, preserved ffprobe/jobs (Slice 4).
- **Backups**: card layouts for alerts/jobs/runs tables (Slice 5).
- **Users**: card layout with selection checkboxes + drawer navigation (Slice 5).
- **Actions**: existing responsive layout + touch-target audit (Slice 9).
- **Services**: list renders stacked; service edit via SheetForm (Slices 6, 9).
- **Settings**: machine editor via SheetForm; existing inline panels stack (Slice 7, 9).
### AC2 — Four wide tables show cards at 375px and tables at 1280px ✅
Media, FileBrowser, UsersPage, and the three Backups tables each render
`MobileCardRow` cards below `md` and `<DataTable>` tables at/above `md`. Each
card shows a primary title + 35 fields chosen per-table. Tested in Vitest
with mocked `matchMedia` at both breakpoints.
### AC3 — Four edit forms open in Sheet at 375px and Dialog at 1280px ✅
ServicePage, Settings (machine editor), message compose, and WidgetConfigDialog
each branch on `useIsMobile()` to render `SheetForm` (side=bottom, full-height)
below `md` and the existing `Dialog` at/above `md`. Tested in Vitest.
### AC4 — HoverEditButton always visible at 375px, hover-revealed at 1280px ✅
`HoverEditButton` defaults to `mobile="always"` (always visible below `md`,
hover-revealed at `md:`+). Tested in HoverEditButton.test.tsx with class-
composition assertions.
### AC5 — 44px minimum touch-target audit ✅
40 interactive elements across 12 files now carry the `mobile-touch-target`
class (applies `min-height: 44px; min-width: 44px` only below 768px). Covers
icon buttons, checkboxes, switches, and small text buttons. Default-size text
buttons (32px) were deliberately skipped to stay surgical — flagged as a
residual risk if strict WCAG 2.5.5 on ALL elements is required.
### AC6 — Dashboard single column + anchors at 375px, grid at 1280px ✅
Tested in Dashboard.test.tsx: mobile test asserts single column + section
labels + anchor pills; desktop test asserts no anchor bar + widgets present.
### AC7 — lint/build/test green ✅
```
cd frontend && npm run lint → 0 errors (2 pre-existing warnings)
cd frontend && npm run build → ✓ built (tsc -b + vite)
cd frontend && npm run test → 28 files / 116 tests passed
```
### AC8 — Vitest test per touched page at <768px and ≥768px ✅
Each touched page has at least one mobile and one desktop test:
| Page/Component | Mobile tests | Desktop tests |
|----------------|-------------|---------------|
| Dashboard | 3 | 3 (existing) |
| Media | 5 | existing |
| FileBrowser | 4 | existing |
| UsersPage | 2 | existing |
| Backups (Alerts/Runs) | 3 | existing |
| BackupJobs | 2 (new file) | — |
| ServicePage | 3 | 2 (new file) |
| Settings | 3 | existing |
| WidgetConfigDialog | 1 | 1 (new file) |
| MobileCardRow | 7 | — (primitive) |
| SheetForm | 5 | — (primitive) |
| HoverEditButton | 2 | 2 |
## Non-goals confirmed
- No tablet/landscape/sm: intermediate layout.
- No PWA, manifest, service worker.
- No polling-interval changes.
- No backend changes.
- No new data-table library.
## Residual risks / known gaps
1. **R4.5 dirty-state outside-click confirm** — RESOLVED. `SheetForm` gained an
`isDirty` prop; when true, any close path (Cancel, header X, Radix overlay
click, Escape) opens a "Discard changes?" confirm. All four form consumers
(ServicePage, Settings machine editor, message compose, WidgetConfigDialog)
compute and pass `isDirty`.
2. **Default-size text buttons (32px)** — RESOLVED. A second touch-target pass
applied `.mobile-touch-target` to 32 default-size buttons across 9 files
(Save, Cancel, Delete, Validate SSH, Run job, etc.) plus the shared
`DialogFooter`. Combined with Slice 9, all interactive elements below `md`
now meet the 44px minimum.
3. **Polling on battery** (D8 risk) — RESOLVED. `refetchIntervalInBackground:
false` is now a `QueryClient` default, so all interval polls (widgets ~30s,
queue status 5s, media build progress 1s) pause when the tab is hidden. The
`useMedia` build-progress poll no longer overrides this. Build progress
resumes and catches up on return.
4. **iOS Safari manual verification** not performed in CI. `h-[100dvh]` on
SheetForm, `position: sticky` behavior, and attachment upload from Files
need real-device testing. The flex-column layout (not `position: sticky`)
avoids the known sticky-inside-transform pitfall. UNRESOLVED — requires a
physical device pass.
5. **Pagination duplication** — RESOLVED. Extracted a shared `TablePagination`
component consumed by both the desktop `DataTable` and the Media mobile
card list. Removes ~90 lines of duplication.