Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions docs/MOBILE_DOCTRINE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Mobile Doctrine

Repo-wide rules for phone-class surfaces (golf, baseball, Bridge). Distilled
from the 2026-07-09 nine-reviewer mobile audit and the owner's brief: "Need
more native and centered — not like a copy-and-paste slim-down"; "too full and
crammed, lotta scrolling, not segmented"; "PREMIUM ARCHITECTURE, EVERY PAGE
HAND-CRAFTED AND THOUGHT OUT — that goes for desktop too."

Execution waves and per-surface grades live in the mobile overhaul plan
(see PR #797 and successors). This file is the standing law; cite the rule
number in reviews.

## The rules

1. **Home = triage, never an aggregator.** A home surface shows the next
action, live counts, and links into detail tabs. It must never re-render
the full body of a destination that owns its own tab.
2. **Above-fold budget.** The primary action is reachable in the first
viewport at 390px. Editorial mastheads (eyebrow + long title + paragraph)
are desktop cover treatments — on phone they condense to one line.
3. **Cap the scroll at ~3 screen-heights.** Beyond that: segmented control,
tabs, collapsed-by-default sections, or push to a route. Empty sections
never render — they roll into one "all caught up" card.
4. **Bottom sheets, not centered modals**, for every input/create flow under
`md`. The vaul Sheet primitive already ships in-repo.
5. **Thumb-zone commits.** Sticky bottom action bar for the primary CTA on
any decision/entry screen; never scroll-to-save.
6. **More-sheet, not left drawer.** The 5th bottom-nav slot opens a
thumb-reachable bottom sheet listing overflow destinations.
Hamburger-top-left dies on phone; the desktop rail is untouched.
7. **No desktop chrome on phones.** ⌘K pills, breadcrumb trails, hover-only
affordances: desktop-only. One condensing chrome band on hub routes, not
three stacked bands.
8. **Tables become cards below `md`.** Any `min-w-[###px]` table on a
phone-primary surface renders as full-width rows (identity + 2–3 key
stats + tap-through).
9. **Tab switches are instant.** No cross-fade between bottom-tab roots;
motion is reserved for forward/detail pushes. Reduced-motion disables all.
10. **Bottom nav = the role's actual daily loop** (4 destinations + More),
declared in the nav registry — a daily destination must never be
overflow-only.
11. **No full-screen monolith cards — every page is composed.** A single
card/panel stretched to the viewport as the screen's whole composition is
banned (same vibe-coded tell-class as the retired accent-stripe cards).
Compose: inset grouped sections, mixed row/card rhythm, deliberate
hierarchy per surface.

## The craft bar (mobile AND desktop)

- Every UI change ships from a **per-surface design brief** — what the screen
is for, its above-fold statement, its rhythm — never "make it responsive."
- Waves get **taste verification** (does it look hand-crafted and native?) in
addition to diff-correctness verification.
- Desktop is judged to the same bar: density, alignment, composition.

## Performance floor (phone-class GPUs)

- No `backdrop-blur` on scrolling chrome below `md` (top bars, bottom navs).
- One route fade, one owner (`template.tsx`); never a second pathname-keyed
fade in the shell.
- framer-motion only via `LazyMotion` + the async loader at
`src/lib/motion/load-features.ts` (domAnimation only — no layout
animations).
- `React.memo` on shell chrome must not be defeated: element props passed
into `AppShell` (`brand`, `sidebarFooter`, `topBarActions`, …) are
memoized at the call site.
- Realtime subscriptions on chrome (badges, unread counts) are filtered to
the current user/team — never org-wide.
8 changes: 3 additions & 5 deletions memory/projects/golfhelm.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ src/lib/coachhelm/
<!-- AUTOGEN:routes:start -->
<!-- DO NOT EDIT — regenerated by scripts/regen-docs.mjs -->

**224 routes** (source: `src/app/**/page.tsx`).
**221 routes** (source: `src/app/**/page.tsx`).

<details><summary>Full alphabetical route list</summary>

Expand Down Expand Up @@ -369,15 +369,12 @@ src/lib/coachhelm/
- `/baseball/dashboard/stats/games`
- `/baseball/dashboard/stats/games/[gameId]`
- `/baseball/dashboard/stats/games/create`
- `/baseball/dashboard/stats/season`
- `/baseball/dashboard/stats/upload`
- `/baseball/dashboard/tasks`
- `/baseball/dashboard/team`
- `/baseball/dashboard/teams`
- `/baseball/dashboard/travel`
- `/baseball/dashboard/videos`
- `/baseball/dashboard/videos/[id]`
- `/baseball/dashboard/videos/[id]/edit`
- `/baseball/dashboard/watchlist`
- `/baseball/demo`
- `/baseball/forgot-password`
Expand Down Expand Up @@ -505,7 +502,7 @@ src/lib/coachhelm/
<!-- AUTOGEN:actions:start -->
<!-- DO NOT EDIT — regenerated by scripts/regen-docs.mjs -->

**182 server-action files** (source: `src/app/**/actions/**/*.ts`).
**183 server-action files** (source: `src/app/**/actions/**/*.ts`).

<details><summary>Full alphabetical action file list</summary>

Expand Down Expand Up @@ -636,6 +633,7 @@ src/lib/coachhelm/
- `src/app/golf/actions/player-feedback.ts`
- `src/app/golf/actions/player-fingerprint-types.ts`
- `src/app/golf/actions/player-fingerprint.ts`
- `src/app/golf/actions/player-hub-data.ts`
- `src/app/golf/actions/player-notifications.ts`
- `src/app/golf/actions/player-profile-stats.ts`
- `src/app/golf/actions/push-notifications.ts`
Expand Down
5 changes: 3 additions & 2 deletions src/app/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import Link from 'next/link';
import Image from 'next/image';
import { m, LazyMotion, domAnimation, useReducedMotion } from 'framer-motion';
import { m, LazyMotion, useReducedMotion } from 'framer-motion';
import { loadFeatures } from '@/lib/motion/load-features';
import { Navigation } from '@/components/landing/Navigation';
import { Footer } from '@/components/landing/Footer';
import { Button } from '@/components/ui/button';
Expand Down Expand Up @@ -61,7 +62,7 @@ const staggerContainer = {
export default function AboutPage() {
const prefersReducedMotion = useReducedMotion();
return (
<LazyMotion features={domAnimation}>
<LazyMotion features={loadFeatures}>
<main className="min-h-dvh bg-background overflow-x-hidden">
<a href="#main-content" className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-primary-600 focus:text-white focus:rounded-lg focus:shadow-lg">
Skip to main content
Expand Down
Loading
Loading