Each player picks ANY avatar from a combined 16-emoji roster (8 sparkle + 8 shred); their color, move sound, and particle FX follow that avatar's CAMP, so one nibling can rock unicorns/rainbows while the other brings skulls/metal. A 3-way STAGE selector (Sparkle / Shred / Mixed) sets the global backdrop, fonts, and winner vibe. Camps and stages are orthogonal, driven by CSS custom properties (.camp-* and [data-stage]). - Combined avatar grids for both players with a same-avatar guard - Per-camp Web Audio SFX (sine chimes vs sawtooth power chords) + mute toggle - Per-player glow on displays, scoreboard, and board pieces; winner screen adopts the winner's camp; screen shake on shred moves; themed celebration - Persist names/avatars/stage/scores/mute via localStorage; reset-scores button - Fix nested-scroll / clipped-top bug (center with margin:auto, not flex + max-height); no horizontal overflow 320px-desktop; SVG favicon - Keyboard-navigable board, ARIA roles, live status, prefers-reduced-motion - Containerize for OrbStack: Dockerfile (nginx:alpine), nginx.conf (gzip + cache/security headers), docker-compose.yml — one command on :8080 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
68 lines
3.4 KiB
Markdown
68 lines
3.4 KiB
Markdown
# Ultimate Tic-Tac-Toe
|
|
|
|
A two-player, hot-seat tic-tac-toe game for the browser where **each player brings their own flavor**.
|
|
|
|
Every player picks **any** avatar from a combined roster — sparkle *or* shred — and that choice sets their personal color, move sound, and particle effects. One player can rock 🦄 unicorns and rainbows while the other brings 💀 skulls and metal. A separate **stage** selector sets the global vibe.
|
|
|
|
## Camps & stages
|
|
|
|
**Camps** (per player, set by the chosen avatar):
|
|
|
|
- **✨ Sparkle** — ⭐🦄🌸🎀🦋🌈🎨🍭 · pink glow, bright sine chimes, sparkle particles.
|
|
- **💀 Shred** — 💀☠️🎸🤘🔥⚡🏴☠️🐉 · red glow, sawtooth power-chords, fire/lightning particles, board shake.
|
|
|
|
**Stages** (global, pick on the setup screen):
|
|
|
|
- **✨ Sparkle** — pastel gradient, glassmorphism cards, twinkling stars.
|
|
- **💀 Shred** — black & red, rising embers, lightning flashes, metal lettering.
|
|
- **⚔️ Mixed** — neutral dark arena where both camps' colors pop; both backdrops play. Great when the two players don't agree.
|
|
|
|
## Features
|
|
|
|
- **Free-for-all avatars** — each player's grid shows all 16; a guard stops both choosing the *same* one.
|
|
- **Per-player identity** carries through the whole game: side panels, scoreboard, and each player's pieces on the board glow in their camp color, with camp-specific move sounds and particle bursts.
|
|
- **Three stages** driven entirely by CSS custom properties (`[data-stage]`); the winner screen adopts the winner's camp.
|
|
- **Web Audio** SFX (no asset files); mute toggle, top-right.
|
|
- **Persistent** names, avatars, stage, scores, and mute via `localStorage`.
|
|
- **Accessible** — keyboard-navigable board (arrow keys + Enter/Space), ARIA roles and live status, visible focus rings, `prefers-reduced-motion`.
|
|
- **Responsive & scroll-safe** — no horizontal overflow or clipped content from 320 px to desktop (the old nested-scroll / clipped-top bug is fixed: the layout now centers with `margin:auto` instead of fragile flex centering + `max-height`).
|
|
|
|
## Run it
|
|
|
|
Static site, no build step.
|
|
|
|
**Local web server** (so `localStorage` + audio behave like production)
|
|
```bash
|
|
python3 -m http.server 8000
|
|
# → http://localhost:8000
|
|
```
|
|
|
|
**Docker / OrbStack** (nginx, production-style headers)
|
|
```bash
|
|
docker compose up -d --build
|
|
# → http://localhost:8080
|
|
docker compose down
|
|
```
|
|
|
|
## Test
|
|
|
|
End-to-end tests drive real Chromium against the running app via `playwright-core` and print a plain-text PASS/FAIL report (avatar roster, mixed-camp play, stage switching, win/draw flows, persistence, responsive overflow at five widths, keyboard a11y, console cleanliness). Screenshots land in `screenshots/`.
|
|
|
|
```bash
|
|
# with a server (or the container) running on :8080
|
|
PW_CORE="$(node -e "console.log(require.resolve('playwright-core'))")" \
|
|
node test/e2e.mjs http://localhost:8080
|
|
# optional: PW_CHROMIUM=/path/to/Chromium to pin a browser build
|
|
```
|
|
|
|
## Project structure
|
|
|
|
```
|
|
index.html markup; stage selector + three screens (setup / game / winner)
|
|
style.css stages ([data-stage]) + per-player camps (.camp-sparkle/.camp-shred)
|
|
script.js engine: avatar roster, camps, stages, audio, FX, persistence, a11y
|
|
Dockerfile nginx:alpine static image
|
|
nginx.conf gzip, cache + security headers
|
|
docker-compose.yml one-command run on :8080 via OrbStack
|
|
test/e2e.mjs Playwright end-to-end suite
|
|
```
|