Skip to content

Fix stale tile colors on theme toggle and double-counted stats on repeat Enter - #213

Merged
dotcomstar merged 1 commit into
mainfrom
animation-fixes
Sep 2, 2026
Merged

Fix stale tile colors on theme toggle and double-counted stats on repeat Enter#213
dotcomstar merged 1 commit into
mainfrom
animation-fixes

Conversation

@dotcomstar

Copy link
Copy Markdown
Owner

Two bugs found while reviewing #212 before deploying it to production. Neither is in production yet; both are on main.

1. Scored tiles froze in the previous theme's colors

Switching dark/light mode (or High Contrast) after scoring a guess left the tiles in the old palette while everything else, including the keyboard keys directly below them, switched.

Before (unfixed main): dark mode, guess CHAIR scored, then switched to light. The tiles keep the dark palette (near-black R, dark green/yellow); the keys show the light one.

Scored tiles stuck in dark-mode colors after switching to light mode

Computed colors at that moment:

element color
C tile rgb(83, 141, 78) (dark success)
C key rgb(106, 170, 100) (light success)
R tile rgb(58, 58, 60) (dark error)
R key rgb(120, 124, 126) (light error)

After: same steps on this branch. Tiles and keys change together and report identical colors.

Tiles following the theme after the fix

Cause. Cell cached the PaletteColor object it was scored with in displayStatus and only refreshed it at the flip midpoint. ThemedLayout rebuilds the theme in place (useMemo, no remount), so the effect saw a new object as a changed status prop but early-returned because the tile was already revealed. Before #212 the tile read status.main directly, so it always tracked the theme.

Fix. A letter's status is now a palette key end to end (LetterStatus = "success" | "warning" | "error" | "primary", exported from Cell.tsx). GameGrid computes keys, GameRow compares keys and uses sx theme paths for the connector color, and Cell resolves theme.palette[displayStatus] on every render. SampleGame and LandingLogo pass keys too, and both drop their useTheme calls. This was chosen over syncing the cached object on change because it removes the class of bug rather than patching one path, and it matches how Keyboard already types key statuses.

The effect also now mirrors the prop whenever the tile is already revealed. That closes a latent hole where any dependency change mid-flip cancelled the pending reveal timer and left the tile uncolored for good.

2. A repeat Enter during the game-end delay double-counted stats

#212 holds the stats dialog back ~2.5 s behind the flip, wave, and confetti on a game-ending win. During that window the on-screen ENTER key is still clickable, and a second tap (a mobile double-tap, say) re-ran the whole game-end block. logGame is additive, so the day's stats were counted twice. Previously the dialog's backdrop appeared synchronously and blocked this. The same double-count already happened on any Enter after dismissing the dialog, or after reloading a finished game.

Fix. The game-end block only ends the game and logs when this render's gameState is still "inProgress". Any later press just reopens the stats dialog.

Tests

  • Cell: renders inside a ThemeProvider, swaps in a theme with a different success.main, asserts the background follows.
  • Cell: a status change on an already-revealed tile updates color and aria text with no flip.
  • HomePage: a repeat Enter after a game-ending win reopens the dialog without changing numQuestionsAttempted, questionsGuessedIn, or the category stats, both inside the delay window and after dismissing the dialog.

npm run lint, npx vitest run (204 passing), npm run build all pass locally. The review doc Claude-notes/code-review-2026-09-02.md has both findings with the screenshots.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JpS4A5zjVECoAQjVfMFazi

…ing stats

Two fixes on top of the animations work (#212):

- Scored tiles froze in the previous theme's colors after a dark-mode or
  colorblind toggle. Cell cached the PaletteColor object it was scored
  with; ThemedLayout rebuilds the theme in place, so the tile's effect saw
  a changed prop but early-returned as already revealed. A letter's status
  is now a palette key (LetterStatus) end to end -- GameGrid computes keys,
  GameRow compares keys and uses sx theme paths, Cell resolves
  theme.palette[displayStatus] on every render. SampleGame and LandingLogo
  pass keys too. The effect also mirrors the prop once revealed, so a
  dependency change mid-flip reveals immediately instead of never.

- A second Enter during the new ~2.5 s game-end delay (or after
  dismissing the stats dialog, or on a reloaded finished game) re-ran the
  game-end block, and logGame is additive, so the day's stats were counted
  twice. The block now only ends the game and logs when this render's
  gameState is still "inProgress"; later presses just reopen the dialog.

Adds a ThemeProvider-swap test and a status-change test for Cell, and a
repeat-Enter test for HomePage. Before/after screenshots of the color
mismatch live in Claude-notes/images and are referenced from the 09-02
review doc.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JpS4A5zjVECoAQjVfMFazi
@vercel

vercel Bot commented Sep 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
triviale Ready Ready Preview Sep 2, 2026 6:46pm UTC

@dotcomstar
dotcomstar merged commit 028aeae into main Sep 2, 2026
3 checks passed
@dotcomstar
dotcomstar deleted the animation-fixes branch September 2, 2026 19:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant