Files
manage/openspec/changes/mobile-responsive-parity/spec.md
T
Developer 18ee77a4e4 Plan mobile responsive parity (OpenSpec change)
Add proposal/spec/design/tasks for full mobile parity across all 9 routes.
Decisions: hybrid tables (cards below md for big four), Sheet-based forms,
always-visible edit affordance, 44px touch targets, single-column dashboard
with anchors, responsive web only (no PWA), phone portrait at md:768px cut.
Polling unchanged (risk flagged). Delivery: 10 chained PRs, primitives first.
2026-06-26 11:49:47 +00:00

5.7 KiB
Raw Blame History

Spec — Mobile responsive parity

Change: mobile-responsive-parity Phase: spec Date: 2026-06-26

Scope

All 9 application routes must be fully operable in phone portrait viewports (≥360px) at a single md: (768px) breakpoint. Tablets and wider viewports keep the existing desktop layout unchanged. No product behavior changes; this is a presentation-layer parity change only.

Requirements

R1 — Viewport & breakpoint policy

  • R1.1 The viewport meta stays width=device-width, initial-scale=1.0 (no zoom lock). User zoom remains enabled.
  • R1.2 There is exactly one responsive cut: md: (768px). Below is "mobile"; at-or-above is "desktop" (existing behavior).
  • R1.3 No sm: intermediate cut is introduced.

R2 — App shell (already compliant; locked in)

  • R2.1 Desktop Sidebar renders null when isMobile (matchMedia("(max-width: 768px)")).
  • R2.2 Mobile nav uses the existing MobileDrawer (hamburger, md:hidden, Sheet side=left) with no behavioral change.
  • R2.3 TopBar keeps its existing responsive behavior (version badges hidden on small screens, hamburger visible below md).

R3 — Data tables (hybrid)

  • R3.1 The four wide tables — Media (pages/Media.tsx), FileBrowser (pages/FileBrowser.impl.tsx), Users (pages/UsersPage.impl.tsx), and the three Backups tables (BackupAlertsTable.tsx, BackupJobsTable.tsx, BackupRunsTable.tsx) — render a stacked card per row below md.
  • R3.2 Each card shows a primary title plus the 35 most important fields for that table (chosen per-table; documented in tasks). All remaining fields are omitted from the mobile card.
  • R3.3 Row click / selection semantics are preserved on the card (tap target = the whole card where applicable).
  • R3.4 SessionActivityPanel (narrow, 3-column) keeps the <table> shape inside a horizontal-scroll container below md.
  • R3.5 The TanStack column-visibility toggle is hidden below md on every table that uses it (Media). The mobile card picks the fields; the user does not re-show hidden columns on touch.
  • R3.6 At md: and above, all tables render exactly as today.

R4 — Edit forms (Sheet)

  • R4.1 Below md, these edit flows open in a full-height Sheet (side=bottom or side=right, full screen) instead of a centered Dialog:
    • ServicePage connection config + secrets
    • Settings machines and SSH-key editors
    • Message compose dialog (UsersPage.impl.tsx)
    • WidgetConfigDialog
  • R4.2 The Sheet form has a sticky header (title + close affordance) and a sticky footer/save bar (Cancel + Save).
  • R4.3 Form fields stack to a single column inside the Sheet.
  • R4.4 At md: and above, the existing Dialog-based forms are unchanged.
  • R4.5 The Sheet closes on successful save and on explicit cancel; it does not close on outside-click while the form is dirty (confirm prompt).

R5 — Touch edit affordance

  • R5.1 HoverEditButton gains a md: variant: hover-revealed on desktop (unchanged), always visible below md.
  • R5.2 Row/card edit and delete actions surface as persistent icon buttons on the right edge below md.
  • R5.3 Desktop hover-reveal aesthetic is not regressed at md: and above.

R6 — Touch targets

  • R6.1 All interactive elements below md have a minimum 44×44px hit area. This includes: buttons, icon buttons, checkboxes, switches, row/card tap targets, and badges that act as buttons.
  • R6.2 Visual size may remain smaller than 44px (padding-only hit areas are acceptable) as long as the tappable region meets the minimum.
  • R6.3 At md: and above, sizes are unchanged.

R7 — Dashboard layout

  • R7.1 The widget grid collapses to a single column below md.
  • R7.2 A section anchor bar appears at the top of the dashboard below md, grouping widgets (e.g. Observability / Media / Backups / Custom) and allowing quick jump-to-section.
  • R7.3 Widget order respects the user's configured sort order.
  • R7.4 At md: and above, the grid renders exactly as today.

R8 — Polling (unchanged)

  • R8.1 Widget refresh intervals and the message-queue poll interval are identical on mobile and desktop.
  • R8.2 (Follow-up risk, not in scope: pause refetch on document.visibilityState === "hidden". Tracked in proposal §Risks.)

R9 — No PWA

  • R9.1 No web manifest, service worker, or standalone display mode is added.
  • R9.2 OIDC continues to work in-browser; no standalone-mode redirect handling is introduced.

R10 — Non-regression

  • R10.1 No desktop layout (≥768px) is visually or functionally regressed.
  • R10.2 No backend API contract change.
  • R10.3 No existing test is deleted; mobile-specific tests are additive.

Acceptance criteria

  • AC1 Every route listed in App.tsx navItems (Dashboard, Observability, Media, Files, Backups, Users, Actions, Services, Settings) is fully operable at 375px width in Chrome DevTools device mode (iPhone 12 Pro preset or equivalent).
  • AC2 Each of the four wide tables shows a card layout at 375px and the table layout at 1280px.
  • AC3 Each of the four edit forms opens in a Sheet at 375px and a Dialog at 1280px.
  • AC4 HoverEditButton is always visible at 375px and hover-revealed at 1280px.
  • AC5 A 44px-minimum touch-target audit passes for all interactive elements at 375px.
  • AC6 The Dashboard renders a single column with an anchor bar at 375px and the existing grid at 1280px.
  • AC7 cd frontend && npm run lint && npm run build && npm run test is green.
  • AC8 At least one Vitest test per touched page asserts behavior at <768px and ≥768px breakpoints.

Non-goals

  • Tablet/landscape/sm: intermediate layout.
  • PWA, manifest, service worker, offline mode.
  • Polling-interval changes.
  • Backend changes.
  • New data-table library.
  • New product features.