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.
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
# 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 3–5 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.
|
||||
Reference in New Issue
Block a user