No description
Find a file
Parley Hatch 0fc1d32099 Make tic-tac-toe epic: per-player camps, 3 stages, containerized
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>
2026-05-31 18:20:39 -06:00
.dockerignore Make tic-tac-toe epic: per-player camps, 3 stages, containerized 2026-05-31 18:20:39 -06:00
.gitignore Make tic-tac-toe epic: per-player camps, 3 stages, containerized 2026-05-31 18:20:39 -06:00
docker-compose.yml Make tic-tac-toe epic: per-player camps, 3 stages, containerized 2026-05-31 18:20:39 -06:00
Dockerfile Make tic-tac-toe epic: per-player camps, 3 stages, containerized 2026-05-31 18:20:39 -06:00
index.html Make tic-tac-toe epic: per-player camps, 3 stages, containerized 2026-05-31 18:20:39 -06:00
nginx.conf Make tic-tac-toe epic: per-player camps, 3 stages, containerized 2026-05-31 18:20:39 -06:00
README.md Make tic-tac-toe epic: per-player camps, 3 stages, containerized 2026-05-31 18:20:39 -06:00
script.js Make tic-tac-toe epic: per-player camps, 3 stages, containerized 2026-05-31 18:20:39 -06:00
style.css Make tic-tac-toe epic: per-player camps, 3 stages, containerized 2026-05-31 18:20:39 -06:00

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)

python3 -m http.server 8000
# → http://localhost:8000

Docker / OrbStack (nginx, production-style headers)

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/.

# 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