Files
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

5.4 KiB
Raw Permalink Blame History

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.