Add mobile responsive primitives (Slice 1)

Foundation for the mobile-responsive-parity change. Adds:
- useIsMobile() hook: single source of truth for the md:768px cut (SSR-safe)
- MobileCardRow<T>: stacked card list for wide tables below md, with getRowId
  stable keys, primary field as title, optional onRowClick + actions slot
- SheetForm: full-height form host (h-[100dvh], flex column, sticky header +
  footer via flex not position:sticky) for mobile edit flows
- HoverEditButton: mobile prop (default 'always') -- always visible below md,
  hover-revealed at md+; desktop aesthetic preserved
- .mobile-touch-target CSS utility: 44x44 min hit area below md (WCAG 2.5.5)
- App.tsx refactored to use useIsMobile(); shell behavior unchanged

Tests cover primary/field rendering, onRowClick, actions slot, empty rows,
no-primary, stable keys (no duplicate-key warning), and all SheetForm
interactions. 86 tests pass; lint/build green.

MobileCardRow key strategy: uses getRowId when provided (falls back to index);
per design §trade-offs, fields are declared per-table to prioritize by mobile
importance rather than auto-derived from column defs.

Refs openspec/changes/mobile-responsive-parity/ (design §Shared primitives,
spec R1/R5/R6, tasks slice 1).
This commit is contained in:
Developer
2026-06-26 12:09:21 +00:00
parent 18ee77a4e4
commit 688a18af22
9 changed files with 501 additions and 15 deletions
+16
View File
@@ -100,3 +100,19 @@ body,
* {
box-sizing: border-box;
}
/*
* Mobile touch-target utility (spec R6.1).
*
* Applies a 44x44px minimum hit area to interactive elements ONLY below the
* `md:` (768px) breakpoint, satisfying WCAG 2.5.5 / Apple HIG on touch devices.
* At md+ the class is inert so desktop sizing is not regressed. Pages sprinkle
* this on icon buttons, checkboxes, switches, and row taps. See OpenSpec
* change `mobile-responsive-parity`, design §`mobile-touch-target`.
*/
@media (max-width: 767px) {
.mobile-touch-target {
min-height: 44px;
min-width: 44px;
}
}