From 3b6bd6815d6159061885a25ff73b55b2387aab5d Mon Sep 17 00:00:00 2001 From: Ross Andrews Date: Wed, 27 May 2026 00:06:46 -0500 Subject: [PATCH] Document pages, data model, and design system in CLAUDE.md Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/App.tsx | 4 +-- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 71f1b11..541da27 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,4 +33,72 @@ npm run build # production build - Game logic (scoring rules, state) lives in `src/game/` - Types shared across the app live in `src/types.ts` - Prefer small, focused components; keep game logic out of UI components -- No unnecessary comments — name things clearly instead \ No newline at end of file +- No unnecessary comments — name things clearly instead + +## Pages and navigation + +Navigation is a simple `useState` state machine in `App.tsx` — no router. + +| Page | Key | Description | +|------|-----|-------------| +| Title screen | `'title'` | Shown on first load (no saved game). "New Game" button navigates to setup. | +| Player setup | `'setup'` | Enter 3–6 player names, then "Start" saves the game and goes to scoresheet. | +| Score sheet | `'scoresheet'` | Shows current scores. "Score Round" → enter scores; "New Game" → clears state, back to title. | +| Enter scores | `'enterscores'` | Form to enter each player's score for the round. "Submit" → back to scoresheet. | + +On mount, `App.tsx` checks `localStorage` for a saved game and jumps straight to `'scoresheet'` if found. + +## Data model + +```ts +type Page = 'title' | 'setup' | 'scoresheet' | 'enterscores' + +interface GameState { + players: string[] // display names, edge-trimmed + rounds: number[][] // rounds[i][j] = player j's score in round i +} +``` + +Persisted to `localStorage` under the key `currentGame`. No game history yet — only one game at a time. + +## Design system + +### Layout + +- Full-window gradient background: `linear-gradient(to bottom, #a8d5e8, #4a9e6a)` on `body` +- All pages have a rounded card (`border-radius: 2rem`, `border: 2px solid black`) inset `1rem` from window edges +- **Title screen**: card background is transparent (gradient shows through) +- **All other pages**: card background is parchment `#f5f0e4` via `PageFrame` + +### PageFrame component + +Shared layout for non-title pages. Props: `title`, `children`, `footer?`. Renders: +- Fixed-position card with header (h1 + diamond rule divider), scrollable content area, optional sticky footer + +Diamond rule: CSS `::before` pseudo-element with `content: '◆'` centered on a thin border line. Color `#c8b99a`. + +### Colors + +| Token | Value | Usage | +|-------|-------|-------| +| Blue | `#a8d5e8` | Primary button background, gradient top | +| Green | `#4a9e6a` | Gradient bottom | +| Parchment | `#f5f0e4` | Page card background | +| Parchment border | `#c8b99a` | Input borders, diamond rule | +| Error red | `#c0392b` | Invalid field border | +| Error bg | `#fff5f5` | Invalid field background | +| Remove red | `#d94f4f` | Remove-player button background | + +### Buttons + +All buttons share the same press animation: `translateY(2px)` + shadow reduction, 80ms ease transition. + +- **Resting shadow**: `0 4px 12px rgba(0,0,0,0.25)` +- **Pressed shadow**: `0 2px 6px rgba(0,0,0,0.2)` +- **`PrimaryButton`**: full-width, blue, `border-radius: 1rem`, `font-size: 1.25rem`, `font-weight: 600`. Accepts `disabled` prop — disabled state: `opacity: 0.45`, no shadow, `cursor: not-allowed`, no press animation. +- **Round buttons**: circular, `border: none`. Add button: `3rem`, blue. Remove button: `2.5rem`, red. + +### Typography + +- `font-family: sans-serif` everywhere (a real web font will be chosen later) +- Global `button` base: `min-height: 2.75rem`, `font-size: 1rem`, `font-family: inherit` \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 9e6457a..b501358 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -32,8 +32,8 @@ export default function App() { setPage('setup') } - function handleStart() { - const newGame: GameState = { players: [], rounds: [] } + function handleStart(players: string[]) { + const newGame: GameState = { players, rounds: [] } saveGame(newGame) setGame(newGame) setPage('scoresheet')