14 plans in 6 waves covering all 68 requirements for the Tournament Engine phase. Includes research (go-libsql, NATS JetStream, Svelte 5 runes, ICM complexity), plan verification (2 iterations), and user feedback (hand-for-hand UX, SEAT-06 reword, re-entry semantics, integration test, DKK defaults, JWT 7-day expiry, clock tap safety). Wave structure: 1: A (scaffold), B (schema) 2: C (auth/audit), D (clock), E (templates), J (frontend scaffold) 3: F (financial), H (seating), M (layout shell) 4: G (player management) 5: I (tournament lifecycle) 6: K (overview/financials), L (players), N (tables/more) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8.7 KiB
Plan J: SvelteKit Frontend Scaffold + Theme + Clients
wave: 2 depends_on: [01-PLAN-A] files_modified:
- frontend/package.json
- frontend/svelte.config.js
- frontend/vite.config.ts
- frontend/tsconfig.json
- frontend/src/app.html
- frontend/src/app.css
- frontend/src/routes/+layout.ts
- frontend/src/lib/theme/catppuccin.css
- frontend/src/lib/ws.ts
- frontend/src/lib/api.ts
- frontend/src/lib/stores/tournament.svelte.ts
- frontend/src/lib/stores/auth.svelte.ts
- frontend/src/routes/login/+page.svelte autonomous: true requirements: [UI-05, UI-06]
Goal
SvelteKit SPA scaffold with Catppuccin Mocha dark theme, WebSocket client with reconnect, HTTP API client, authentication state, tournament state store, and PIN login page. This is the foundation that the layout shell (Plan M) and all feature views build on.
Context
- SvelteKit with adapter-static — SPA mode, embedded in Go binary via go:embed
- Svelte 5 runes — $state, $derived, $effect for all reactivity (not stores)
- Catppuccin Mocha dark theme default, Latte light theme alternate
- Mobile-first with responsive desktop layout (sidebar instead of bottom tabs)
- 48px minimum touch targets — poker room environment, TD using phone with one hand
- See 01-RESEARCH.md: Svelte 5 Runes WebSocket State, Catppuccin Mocha Theme Setup, Pattern 3 (SvelteKit SPA)
User Decisions (from CONTEXT.md)
- Overview tab priority — Clock > Time to break > Player count > Table balance > Financial summary > Activity feed
- Mobile-first bottom tab bar — Overview, Players, Tables, Financials, More
- FAB for quick actions — Bust, Buy In, Rebuy, Add-On, Pause/Resume
- Persistent header showing clock, level, blinds, player count
- Desktop/laptop sidebar with wider content area
- Catppuccin Mocha dark theme (default), Latte light
- 48px minimum touch targets, press-state animations, loading states
- Toast notifications (success, info, warning, error) with auto-dismiss
- Multi-tournament switching — tabs at top (phone) or split view (tablet landscape)
Tasks
**1. SvelteKit Project Setup:** - Initialize SvelteKit project in `frontend/` directory: - `npm create svelte@latest frontend -- --template skeleton` - Install adapter-static: `npm install -D @sveltejs/adapter-static` - Install Catppuccin: `npm install @catppuccin/palette` - Configure TypeScript-
svelte.config.js:import adapter from '@sveltejs/adapter-static'; export default { kit: { adapter: adapter({ pages: 'build', assets: 'build', fallback: 'index.html', // SPA fallback precompress: false, strict: true }), paths: { base: '' } } }; -
frontend/src/routes/+layout.ts:export const prerender = true; export const ssr = false; // SPA mode, no SSR
2. Catppuccin Theme (frontend/src/lib/theme/catppuccin.css) — UI-05:
-
Define CSS custom properties for Mocha and Latte flavors (all 26 Catppuccin colors)
-
Use
[data-theme="mocha"]and[data-theme="latte"]selectors -
Default to Mocha (dark theme)
-
Include semantic color mappings:
--color-bg: base--color-surface: surface0--color-surface-hover: surface1--color-text: text--color-text-secondary: subtext1--color-primary: blue--color-success: green--color-warning: yellow--color-error: red--color-accent: mauve- Poker-specific:
--color-felt: green,--color-card: text,--color-bounty: pink,--color-prize: yellow
-
Typography: system-ui for body, monospace for timers/numbers
-
Base styles: body background, text color, font size, line height
-
app.css:- Import catppuccin.css
- Reset styles (box-sizing, margin, padding)
- Touch action:
touch-action: manipulationon interactive elements (prevent double-tap zoom) - Minimum touch target:
.touch-target { min-height: 48px; min-width: 48px; }— UI-06 - Active/pressed states:
:active { transform: scale(0.97); opacity: 0.9; }— UI-06 - Focus visible styles for accessibility
- Scrollbar styling for dark theme
3. WebSocket Client (frontend/src/lib/ws.ts):
WebSocketClientclass:- Connect to
ws://host/ws(auto-detect protocol for HTTPS → WSS) - Auto-reconnect with exponential backoff (1s, 2s, 4s, 8s, max 30s)
- Connection state tracking: connecting, connected, disconnected, reconnecting
send(message)— JSON serialize and sendsubscribe(tournamentID)— send subscription messageonMessage(callback)— register message handleronStateChange(callback)— register connection state handler- Parse incoming messages:
{type: string, data: any} - Route messages to the tournament state store
- Connect to
4. HTTP API Client (frontend/src/lib/api.ts):
apiobject with methods:get(path),post(path, body),put(path, body),delete(path)- Base URL: auto-detect from current host
- Automatically attach JWT from auth store as
Authorization: Bearerheader - Handle 401 responses: clear auth state, redirect to login
- Handle errors: parse error response, throw typed errors
- Response parsing: auto-parse JSON
- Loading state tracking (for UI loading indicators)
5. Auth State (frontend/src/lib/stores/auth.svelte.ts):
- Using Svelte 5 runes:
class AuthState { token = $state<string | null>(null); operator = $state<Operator | null>(null); get isAuthenticated() { return this.token !== null; } get isAdmin() { return this.operator?.role === 'admin'; } get isFloor() { return ['admin', 'floor'].includes(this.operator?.role ?? ''); } login(token: string, operator: Operator) { ... } logout() { ... } } export const auth = new AuthState();- Persist token to localStorage
- Load token from localStorage on app startup
- Clear on logout or 401 response
6. Tournament State (frontend/src/lib/stores/tournament.svelte.ts):
- Using Svelte 5 runes per 01-RESEARCH.md pattern:
class TournamentState { clock = $state<ClockSnapshot | null>(null); players = $state<Player[]>([]); tables = $state<Table[]>([]); financials = $state<FinancialSummary | null>(null); activity = $state<ActivityEntry[]>([]); rankings = $state<PlayerRanking[]>([]); balanceStatus = $state<BalanceStatus | null>(null); get remainingPlayers() { return this.players.filter(p => p.status === 'active').length; } get isBalanced() { ... } handleMessage(msg: WSMessage) { switch (msg.type) { case 'clock.tick': this.clock = msg.data; break; case 'player.bust': ... break; case 'state.snapshot': this.loadFullState(msg.data); break; // ... all message types } } } export const tournament = new TournamentState();
7. Login Page (frontend/src/routes/login/+page.svelte):
- PIN input with 4 large digit buttons (48px+ touch targets)
- PIN display with dots (masked)
- Submit button
- Error display for wrong PIN / rate limited
- Auto-redirect to overview on successful login
- Catppuccin Mocha styling
8. Update Makefile:
- Update
make frontendtarget to actually build the SvelteKit project:cd frontend && npm install && npm run build
- Update
make allto build frontend first, then Go binary
Verification:
cd frontend && npm run buildproducesfrontend/build/index.htmlmake allbuilds the full binary with embedded frontend- Login page renders with dark theme
- WebSocket client connects and logs messages to console
- API client sends requests with JWT header
Verification Criteria
- SvelteKit builds to static SPA with
npm run build - Catppuccin Mocha dark theme applied with CSS custom properties
- 48px minimum touch targets defined in base styles
- WebSocket client connects with auto-reconnect and exponential backoff
- HTTP API client sends requests with JWT Authorization header
- Auth state persists to localStorage and clears on 401
- Tournament state store handles all WebSocket message types
- Login page works with PIN authentication
make allbuilds the full binary with embedded frontend
Must-Haves (Goal-Backward)
- Catppuccin Mocha dark theme as default with semantic color variables
- 48px minimum touch targets on all interactive elements (base CSS)
- WebSocket client with auto-reconnect for real-time updates
- HTTP API client with JWT auth and error handling
- Tournament state store reactive via Svelte 5 runes
- Auth state with localStorage persistence
- PIN login page