Document pages, data model, and design system in CLAUDE.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -33,4 +33,72 @@ npm run build # production build
|
|||||||
- Game logic (scoring rules, state) lives in `src/game/`
|
- Game logic (scoring rules, state) lives in `src/game/`
|
||||||
- Types shared across the app live in `src/types.ts`
|
- Types shared across the app live in `src/types.ts`
|
||||||
- Prefer small, focused components; keep game logic out of UI components
|
- Prefer small, focused components; keep game logic out of UI components
|
||||||
- No unnecessary comments — name things clearly instead
|
- No unnecessary comments — name things clearly instead
|
||||||
|
|
||||||
|
## Pages and navigation
|
||||||
|
|
||||||
|
Navigation is a simple `useState<Page>` 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`
|
||||||
+2
-2
@@ -32,8 +32,8 @@ export default function App() {
|
|||||||
setPage('setup')
|
setPage('setup')
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleStart() {
|
function handleStart(players: string[]) {
|
||||||
const newGame: GameState = { players: [], rounds: [] }
|
const newGame: GameState = { players, rounds: [] }
|
||||||
saveGame(newGame)
|
saveGame(newGame)
|
||||||
setGame(newGame)
|
setGame(newGame)
|
||||||
setPage('scoresheet')
|
setPage('scoresheet')
|
||||||
|
|||||||
Reference in New Issue
Block a user