Skip to content

Bump coin count on gain, dim hint button when unaffordable, note replays give no coins - #89

Merged
min0625 merged 1 commit into
mainfrom
ui-coin-bump-hint-poor-replay-note
Jul 28, 2026
Merged

min0625 merged 1 commit into
mainfrom
ui-coin-bump-hint-poor-replay-note

Conversation

@min0625

@min0625 min0625 commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Summary

  • Top-bar coin number plays a bump animation when coins increase (bonus/claim), not on spend (hint) — updateCoins compares against the last rendered value.
  • .screen caps at 480px and centers on desktop/tablet — the wheel is capped at 330px so wide viewports otherwise leave the layout lopsided; no effect on mobile widths.
  • Hint button bulb + price dim together (.hint-btn.poor) once coins drop below hint cost, so affordability reads before pressing, not only via the existing shake-on-press.
  • Entering an already-completed level flashes "重玩不再獲得金幣" once via the existing preview/status flash mechanism.
  • Hint button now gets a proper aria-label (hintLabel, price included) instead of a bare "提示" that silently overrode the visible "−25".

Docs updated: design doc §7/§8/§16/§17, UI doc §2/§3/copy table, CLAUDE.md, .serena/memories/project-overview.md.

Test plan

  • mise run check (lint + 48 unit tests) passes
  • Manually verified in desktop Chrome at real viewport sizes
  • Real-device / VoiceOver verification (5 items added to design doc §17 acceptance checklist for 2026-07-28 patches)

…ays give no coins

Four small UI patches:
- Top-bar coin number plays a bump animation when coins increase (not on spend)
- .screen caps at 480px and centers on desktop/tablet, where the wheel's
  330px cap otherwise leaves a lot of empty space on wide viewports
- Hint button bulb + price dim together when coins < hint cost, so
  affordability is visible before pressing, not just via shake-on-press
- Replaying a completed level flashes "重玩不再獲得金幣" once on entry

Also gives the hint button a proper aria-label (hintLabel, price included)
since aria-label was overriding the visible "−25" with just "提示".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Improve coin, hint, and replay feedback

✨ Enhancement 🐞 Bug fix 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Animate coin gains and visually indicate when hints are unaffordable.
• Center wide-screen gameplay and warn that completed-level replays earn no coins.
• Include hint pricing in screen-reader labels and harden reusable animation cleanup.
Diagram

graph TD
  A["Coin mutation"] --> B["Update coins"] --> C{"Coins increased?"} -->|Yes| D["Bump total"]
  B --> E{"Hint affordable?"} -->|No| F["Dim hint"]
  G["Completed level"] --> H["Replay notice"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use the Web Animations API
  • ➕ Avoids CSS-class cleanup and bubbled animation event handling
  • ➕ Provides direct cancellation and finished-state promises
  • ➖ Requires migrating existing pop and shake animations
  • ➖ Needs explicit reduced-motion behavior instead of reusing the global CSS policy
  • ➖ Would create a larger, riskier change for a small set of animations
2. Disable unaffordable hints
  • ➕ Provides native disabled semantics
  • ➕ Prevents futile click handling and shake animation
  • ➖ Removes the existing explanatory insufficient-coins feedback
  • ➖ A disabled control may be skipped or provide insufficient context to assistive technology
  • ➖ Changes established interaction behavior beyond the intended visual affordance

Recommendation: Keep the PR's CSS-class animation and non-disabled hint approach. It integrates with existing animations, reduced-motion handling, and insufficient-funds feedback; the strengthened playOnce lifecycle addresses the primary class-based animation risks. Consider the Web Animations API only if the animation catalog grows enough to justify a coordinated migration.

Files changed (8) +128 / -9

Enhancement (2) +101 / -2
main.jsDrive coin, hint, and replay feedback from application state +62/-1

Drive coin, hint, and replay feedback from application state

• Enhances one-shot animation handling for hidden elements, cancellation, re-entry, and bubbled events. Coin updates now animate gains and toggle hint affordability, while completed-level entry flashes the replay warning and the hint label includes its configured price.

src/main.js

style.cssAdd centered layout and state-driven UI feedback styles +39/-1

Add centered layout and state-driven UI feedback styles

• Caps and centers screens at 480px on wider viewports, adds the coin bump animation, and dims the hint bulb and price when unaffordable. The new animation remains covered by the global reduced-motion policy.

src/style.css

Bug fix (2) +5 / -1
index.htmlDelegate the hint accessible name to runtime configuration +2/-1

Delegate the hint accessible name to runtime configuration

• Removes the hard-coded hint aria-label so JavaScript can generate an accessible name containing the configured coin price.

index.html

strings.jsAdd the price-aware hint accessibility label +3/-0

Add the price-aware hint accessibility label

• Adds a localized hintLabel formatter that announces the configured coin cost to screen-reader users.

src/strings.js

Documentation (4) +22 / -6
project-overview.mdRecord the July UI feedback patches +2/-1

Record the July UI feedback patches

• Updates the project overview with the coin animation, centered desktop layout, unaffordable hint styling, and replay notice. It also records the remaining real-device and VoiceOver verification work.

.serena/memories/project-overview.md

CLAUDE.mdDocument coin gain feedback conventions +1/-1

Document coin gain feedback conventions

• Clarifies that bonus coin feedback now includes a top-bar total animation while preserving the project's no-sound-effects decision.

CLAUDE.md

word-puzzle-game-design.mdExpand UI behavior and acceptance criteria +13/-3

Expand UI behavior and acceptance criteria

• Documents coin gain animation, unaffordable hint styling, and replay messaging behavior. Adds real-device and VoiceOver acceptance checks and records the separate decision not to regenerate levels to remove singular/plural pairs.

docs/word-puzzle-game-design.md

word-puzzle-ui-screens.mdSpecify responsive, affordability, replay, and accessibility behavior +6/-1

Specify responsive, affordability, replay, and accessibility behavior

• Documents the centered 480px screen layout, gain-only coin animation, hint poor state, and transient replay notice. Adds the price-aware hint accessibility label to the copy table and explains the associated UX tradeoffs.

docs/word-puzzle-ui-screens.md

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (1) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 80 rules
✅ Skills: frontend-design

Grey Divider


Action required

1. Replay notice contradicts rewards 🐞 Bug ≡ Correctness
Description
The new replay notice says replays award no coins, but an uncollected bonus word still increases
save.coins during a replay. Players can therefore receive persistent coins immediately after being
told that replaying cannot earn them.
Code

src/main.js[366]

+  if (replay) flashPreview(strings.replayNote, 'dup');
Evidence
A level can be completed while leaving bonus words uncollected because winning checks only target
words. Replay initialization restores only bonus words already found; any remaining bonus returns a
coin reward which onSubmit credits without a replay guard, contradicting the displayed
replayNote.

src/main.js[347-350]
src/game.js[70-70]
src/game.js[97-100]
src/main.js[437-442]
src/strings.js[30-30]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The replay notice promises that replaying awards no coins, but completed levels can retain uncollected bonus words and the replay bonus path still credits those coins. Align the notice with the established one-time bonus behavior, or explicitly prevent every replay coin payout if that is the intended contract; add regression coverage for a completed level with an unclaimed bonus.

## Issue Context
Winning requires only target words. A replay restores previously claimed bonus words, so an unclaimed bonus remains rewardable and `onSubmit` credits it without checking `replay`.

## Fix Focus Areas
- src/main.js[347-350]
- src/main.js[366-366]
- src/main.js[437-442]
- src/strings.js[30-30]
- tests/game.test.mjs[91-97]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Added comments are non-English 📘 Rule violation ⚙ Maintainability
Description
New comments in index.html, src/main.js, src/strings.js, and src/style.css contain Chinese
natural-language explanations. Source comments must be written in English under the compliance rule.
Code

src/main.js[18]

+const animating = new WeakMap(); // el → 上一次播放的 { cls, ac }(見下)
Evidence
The rule prohibits non-English natural-language text in source comments. The cited additions include
Chinese HTML, JavaScript, and CSS comments, such as the inline explanation attached to animating
and the comments describing hintLabel, the coin animation, and unaffordable hint styling.

Rule 2366288: Source code comments must be written in English
index.html[96-96]
src/main.js[18-32]
src/main.js[41-56]
src/main.js[142-143]
src/main.js[255-267]
src/main.js[360-365]
src/strings.js[12-13]
src/style.css[139-148]
src/style.css[211-216]
src/style.css[313-313]
src/style.css[409-415]
src/style.css[906-906]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Translate all natural-language comments added or modified by this PR from Chinese into English without changing runtime behavior.

## Issue Context
PR Compliance ID 2366288 requires source-code comments to be written in English. Preserve code identifiers, selectors, and technical meaning while translating the comments.

## Fix Focus Areas
- index.html[96-96]
- src/main.js[18-32]
- src/main.js[41-56]
- src/main.js[142-143]
- src/main.js[255-267]
- src/main.js[360-365]
- src/strings.js[12-13]
- src/style.css[139-148]
- src/style.css[211-216]
- src/style.css[313-313]
- src/style.css[409-415]
- src/style.css[906-906]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Replay status remains stale 🐞 Bug ≡ Correctness
Description
Starting a wheel gesture during the replay flash cancels its cleanup timer but leaves #status
containing the replay message indefinitely. Re-entering the same replay then writes an unchanged
live-region value, so assistive technology may not announce the notice again.
Code

src/main.js[366]

+  if (replay) flashPreview(strings.replayNote, 'dup');
Evidence
flashPreview writes #status and schedules its removal through previewTimer, while
showPreview cancels that timer without clearing #status. The wheel invokes onChange from
pointerdown, making this stale state reachable immediately after entering a replay.

src/main.js[273-294]
src/wheel.js[161-172]
index.html[201-201]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
When wheel interaction supersedes the replay flash, clear both its visual preview state and live-region status. Ensure entering the same replay again produces a fresh status mutation that assistive technology can announce.

## Issue Context
`flashPreview` relies on `previewTimer` to clear both elements. `showPreview` cancels that timer on the wheel's pointerdown-driven `onChange`, but currently updates only `#preview`.

## Fix Focus Areas
- src/main.js[273-294]
- src/main.js[366-366]
- src/wheel.js[161-172]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread src/main.js
// 播一次動畫 class,播完就拆掉:留著的話切到關卡選擇(screen 用 hidden → display:none)
// 再切回來會讓 CSS 動畫整個重播,看起來像按鈕自己動了一下。
// 先移除→強制回流→加回,否則同一個 class 還在時動畫不會重播。
const animating = new WeakMap(); // el → 上一次播放的 { cls, ac }(見下)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

1. Added comments are non-english 📘 Rule violation ⚙ Maintainability

New comments in index.html, src/main.js, src/strings.js, and src/style.css contain Chinese
natural-language explanations. Source comments must be written in English under the compliance rule.
Agent Prompt
## Issue description
Translate all natural-language comments added or modified by this PR from Chinese into English without changing runtime behavior.

## Issue Context
PR Compliance ID 2366288 requires source-code comments to be written in English. Preserve code identifiers, selectors, and technical meaning while translating the comments.

## Fix Focus Areas
- index.html[96-96]
- src/main.js[18-32]
- src/main.js[41-56]
- src/main.js[142-143]
- src/main.js[255-267]
- src/main.js[360-365]
- src/strings.js[12-13]
- src/style.css[139-148]
- src/style.css[211-216]
- src/style.css[313-313]
- src/style.css[409-415]
- src/style.css[906-906]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/main.js
// +冷卻中的「3:59」已經逼到極限),不該為只在破關那一刻才有後果的訊息花那個位置。
// 要在 showScreen 之後——#preview 所屬的 screen 還 hidden 時 900ms 就走完了,等於沒播。
// 代價:中途切走再回來的人看不到。可接受——是他自己從關卡選擇點進來的,而過關卡片會再說一次。
if (replay) flashPreview(strings.replayNote, 'dup');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Replay notice contradicts rewards 🐞 Bug ≡ Correctness

The new replay notice says replays award no coins, but an uncollected bonus word still increases
save.coins during a replay. Players can therefore receive persistent coins immediately after being
told that replaying cannot earn them.
Agent Prompt
## Issue description
The replay notice promises that replaying awards no coins, but completed levels can retain uncollected bonus words and the replay bonus path still credits those coins. Align the notice with the established one-time bonus behavior, or explicitly prevent every replay coin payout if that is the intended contract; add regression coverage for a completed level with an unclaimed bonus.

## Issue Context
Winning requires only target words. A replay restores previously claimed bonus words, so an unclaimed bonus remains rewardable and `onSubmit` credits it without checking `replay`.

## Fix Focus Areas
- src/main.js[347-350]
- src/main.js[366-366]
- src/main.js[437-442]
- src/strings.js[30-30]
- tests/game.test.mjs[91-97]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/main.js
// +冷卻中的「3:59」已經逼到極限),不該為只在破關那一刻才有後果的訊息花那個位置。
// 要在 showScreen 之後——#preview 所屬的 screen 還 hidden 時 900ms 就走完了,等於沒播。
// 代價:中途切走再回來的人看不到。可接受——是他自己從關卡選擇點進來的,而過關卡片會再說一次。
if (replay) flashPreview(strings.replayNote, 'dup');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Replay status remains stale 🐞 Bug ≡ Correctness

Starting a wheel gesture during the replay flash cancels its cleanup timer but leaves #status
containing the replay message indefinitely. Re-entering the same replay then writes an unchanged
live-region value, so assistive technology may not announce the notice again.
Agent Prompt
## Issue description
When wheel interaction supersedes the replay flash, clear both its visual preview state and live-region status. Ensure entering the same replay again produces a fresh status mutation that assistive technology can announce.

## Issue Context
`flashPreview` relies on `previewTimer` to clear both elements. `showPreview` cancels that timer on the wheel's pointerdown-driven `onChange`, but currently updates only `#preview`.

## Fix Focus Areas
- src/main.js[273-294]
- src/main.js[366-366]
- src/wheel.js[161-172]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@min0625
min0625 merged commit b092a00 into main Jul 28, 2026
1 check passed
@min0625
min0625 deleted the ui-coin-bump-hint-poor-replay-note branch July 28, 2026 14:51
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