Session 37: Design system Phase F — mobile parity: 5-tab bar, More sheet, PWA polish (1872 tests)

VYNDR 2.0 conversion, Phase F (mobile is the PWA we launch first). Frontend-only;
zero backend changes.

- BottomTabBar rewritten to the §6 5-tab spec: Slate/Terminal/Scan/Ledger/More,
  with Scan as the prominent raised grade-green action. Shown for anon too (only
  mobile nav). Integrated More bottom sheet (sheet-up, backdrop dismiss, 48px mono
  rows). iOS safe-area + 44px touch targets.
- Nav hamburger retired on mobile (tab bar owns nav).
- globals.css mobile section: tab-bar hidden >=768, main bottom padding,
  grade-hero 80px, terminal-grid stacks, game-lines horizontal scroll.
- PWA: manifest shortcuts (Slate/Scan/Terminal) + categories; viewport-fit=cover.

Gotcha: `as const` on the TABS array broke type-check (distinct literal types);
fixed with a shared TabDef interface.

19 new tests. Backend 1853 -> 1872, 145 suites, zero regressions. Web build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kev
2026-06-16 10:11:52 -04:00
parent 612f5e0b72
commit f88961885c
10 changed files with 433 additions and 141 deletions
+43
View File
@@ -1221,3 +1221,46 @@ html[data-font="readable"] {
}
html[data-font="readable"] .wm::before,
html[data-font="readable"] .wm::after { opacity: 0.45 !important; }
/* ============================================================
MOBILE PARITY (§6, §F — Session 37)
Mobile is the primary (PWA) layout; desktop is the enhancement.
============================================================ */
/* The 5-tab bar is mobile-only — hidden once the desktop nav has room. */
@media (min-width: 768px) {
.mobile-tab-bar { display: none !important; }
}
/* Clear the fixed bottom tab bar (64px + iOS safe area) so content isn't
hidden behind it. Desktop keeps the standard 80px footer breathing room. */
@media (max-width: 767px) {
main {
padding-bottom: calc(84px + env(safe-area-inset-bottom, 0px)) !important;
}
}
/* Grade hero zone: full-width, slightly smaller letter on phones. */
@media (max-width: 640px) {
.grade-hero { font-size: 80px !important; }
}
/* Book-line tables scroll horizontally on small screens with a sticky
first (team/book) column so the matchup stays anchored while prices scroll. */
@media (max-width: 640px) {
.game-lines-grid {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.game-lines-grid .team-col {
position: sticky;
left: 0;
z-index: 1;
background: var(--bg-1);
}
}
/* Terminal's multi-column grid stacks on mobile. */
@media (max-width: 768px) {
.terminal-grid { grid-template-columns: 1fr !important; }
}
+3
View File
@@ -89,6 +89,9 @@ export const viewport: Viewport = {
width: 'device-width',
initialScale: 1,
maximumScale: 5,
// Session 37 — extend under the iOS notch / home indicator so the
// mobile tab bar's env(safe-area-inset-*) padding has room to work.
viewportFit: 'cover',
};
export default async function RootLayout({ children }: { children: React.ReactNode }) {
+1 -1
View File
@@ -114,7 +114,7 @@ export default function TerminalPage() {
))}
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))', gap: 20 }}>
<div className="terminal-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))', gap: 20 }}>
{/* INJURY CASCADES */}
<div>
<SectionHead style={{ marginBottom: 12 }}>INJURY WIRE · CASCADE ANALYSIS</SectionHead>