arena: BUY CHARM button keeps priority-bounce animation playing while disabled, masking disabled state #184

Closed
opened 2026-05-08 01:41:55 +00:00 by Brouie · 5 comments
Brouie commented 2026-05-08 01:41:55 +00:00 (Migrated from gitlab.com)

tldr

primary CTA buttons with class btn-primary--priority (most prominently the BUY CHARM button on /timecurve/arena) keep playing the priority-bounce keyframe animation even when the button is in :disabled state. the bounce reads as "interactable" stronger than the existing opacity/grayscale dim reads as "not interactable", so users mistake the disabled button for clickable, click it, and only THEN learn it was disabled (via a submit-time error).

found while dogfooding wrong-chain detection on local QA stack — header chip flipped to "Wrong Network" correctly, but BUY CHARM kept bouncing rhythmically, looking inviting. clicked it, got submit-time "Wallet or network changed during purchase" error.

repro

  1. connect wallet to /timecurve/arena
  2. switch MetaMask to a non-31337 chain (any other chain — BNB / Mainnet / etc)
  3. observe header chip correctly flips to "Wrong Network"
  4. observe BUY CHARM button: visually dimmed slightly, but STILL bouncing
  5. click BUY CHARM
  6. submit-time error: "Wallet or network changed during purchase — please retry from the beginning"

functional protection works (no rogue tx broadcast — the chainMismatch in the disabled cascade at TimeCurveArenaView.tsx:866 catches it). UX is reactive instead of preventative.

source

current CSS state in frontend/src/index.css:

  • line 2876 — .btn-primary:disabled adds opacity: 0.65 + cursor: not-allowed
  • line 2886 — .btn-primary--priority adds animation: priority-bounce 3.1s var(--motion-bounce) infinite
  • line 7760 — .timecurve-simple__cta--arcade:disabled adds filter: grayscale(0.45) brightness(0.96)

so the button DOES get dimmed + grayscaled when disabled. but the bounce animation from the priority class keeps running because there's no :disabled rule cancelling it. the bounce reads stronger than the dim does.

fix shape

add a .btn-primary--priority:disabled rule (or extend an existing disabled rule) that cancels the bounce animation. one-line CSS addition:

.btn-primary--priority:disabled {
  animation: none;
}

placed adjacent to the existing .btn-primary--priority rule at line 2886, or grouped with the other disabled-state rules.

optional second pass: tighten the :disabled opacity from 0.65 to ~0.5 to make the dim more decisive, AND add an additional grayscale on the base .btn-primary:disabled so it's not just the arcade variant that fully grayscales. but the bounce-cancel alone would close 90% of the gap.

scope

affects every button using .btn-primary--priority class, not just BUY CHARM. that's the canonical "this is the main action" CTA across the app — Buy CHARM, possibly some other prize-claim or finalize buttons. fix is uniform across all of them.

severity

P3 polish. functional protection is solid (#1 priority defense — no rogue tx). pure UX issue that adds friction to an unhappy path.

cc @PlasticDigits — found during arena dogfood. happy to ship the fix MR for this in same session as filing.

## tldr primary CTA buttons with class `btn-primary--priority` (most prominently the BUY CHARM button on /timecurve/arena) keep playing the `priority-bounce` keyframe animation even when the button is in `:disabled` state. the bounce reads as "interactable" stronger than the existing opacity/grayscale dim reads as "not interactable", so users mistake the disabled button for clickable, click it, and only THEN learn it was disabled (via a submit-time error). found while dogfooding wrong-chain detection on local QA stack — header chip flipped to "Wrong Network" correctly, but BUY CHARM kept bouncing rhythmically, looking inviting. clicked it, got submit-time "Wallet or network changed during purchase" error. ## repro 1. connect wallet to /timecurve/arena 2. switch MetaMask to a non-31337 chain (any other chain — BNB / Mainnet / etc) 3. observe header chip correctly flips to "Wrong Network" 4. observe BUY CHARM button: visually dimmed slightly, but STILL bouncing 5. click BUY CHARM 6. submit-time error: "Wallet or network changed during purchase — please retry from the beginning" functional protection works (no rogue tx broadcast — the chainMismatch in the disabled cascade at TimeCurveArenaView.tsx:866 catches it). UX is reactive instead of preventative. ## source current CSS state in `frontend/src/index.css`: - line 2876 — `.btn-primary:disabled` adds `opacity: 0.65` + `cursor: not-allowed` - line 2886 — `.btn-primary--priority` adds `animation: priority-bounce 3.1s var(--motion-bounce) infinite` - line 7760 — `.timecurve-simple__cta--arcade:disabled` adds `filter: grayscale(0.45) brightness(0.96)` so the button DOES get dimmed + grayscaled when disabled. but the bounce animation from the priority class keeps running because there's no `:disabled` rule cancelling it. the bounce reads stronger than the dim does. ## fix shape add a `.btn-primary--priority:disabled` rule (or extend an existing disabled rule) that cancels the bounce animation. one-line CSS addition: ```css .btn-primary--priority:disabled { animation: none; } ``` placed adjacent to the existing `.btn-primary--priority` rule at line 2886, or grouped with the other disabled-state rules. optional second pass: tighten the `:disabled` opacity from 0.65 to ~0.5 to make the dim more decisive, AND add an additional grayscale on the base `.btn-primary:disabled` so it's not just the arcade variant that fully grayscales. but the bounce-cancel alone would close 90% of the gap. ## scope affects every button using `.btn-primary--priority` class, not just BUY CHARM. that's the canonical "this is the main action" CTA across the app — Buy CHARM, possibly some other prize-claim or finalize buttons. fix is uniform across all of them. ## severity P3 polish. functional protection is solid (#1 priority defense — no rogue tx). pure UX issue that adds friction to an unhappy path. cc @PlasticDigits — found during arena dogfood. happy to ship the fix MR for this in same session as filing.
Brouie commented 2026-05-08 01:50:01 +00:00 (Migrated from gitlab.com)

mentioned in commit d4a6b04544

mentioned in commit d4a6b045445c07760dbbb6e70ce7b2b20b0aa9e6
Brouie commented 2026-05-08 01:50:52 +00:00 (Migrated from gitlab.com)

mentioned in merge request !382

mentioned in merge request !382
PlasticDigits commented 2026-05-09 03:55:30 +00:00 (Migrated from gitlab.com)

mentioned in commit 1163a7ffc1

mentioned in commit 1163a7ffc127eff7a191786aeffc1a3ff2ebea3c
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-09 03:55:30 +00:00
PlasticDigits commented 2026-05-11 09:05:06 +00:00 (Migrated from gitlab.com)

mentioned in issue #164

mentioned in issue #164
PlasticDigits commented 2026-05-13 15:13:36 +00:00 (Migrated from gitlab.com)

mentioned in commit ec252b68b6

mentioned in commit ec252b68b62f9d68d773fed0b15b6a85c5c20dbe
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/yieldomega#184
No description provided.