[visual][arena] BUY CHARM disabled state on wrong-chain renders enabled (cascade vs visual mismatch) #194

Closed
opened 2026-05-11 09:11:29 +00:00 by Brouie · 6 comments
Brouie commented 2026-05-11 09:11:29 +00:00 (Migrated from gitlab.com)

Summary

During local-stack visual QA (#164 §5 item 4), the BUY CHARM button on Arena renders visually as enabled even when the connected wallet is on the wrong chain. Clicking surfaces the friendly Wallet or network changed during purchase — please retry from the beginning revert at submit time — so the contract-level protection works (no rogue tx), but the disabled state is reactive rather than preventative in the UI.

Other Arena post-end CTAs (End sale / Redeem charms / Distribute prizes at TimeCurveArenaView.tsx:1168/1177/1186) properly disable visually on chainMismatch. The BUY CHARM button at TimeCurveArenaView.tsx:861 already includes chainMismatch in its disabled-prop cascade (line 866) per source — the gap is purely visual / disabled-state styling.

P3 polish — preventative UX consistency, not a security issue.

What is wrong

On /timecurve/arena, with wallet connected to a chain other than 31337:

  • End sale / Redeem charms / Distribute prizes correctly render visually disabled with helper title
  • BUY CHARM renders visually as enabled (no dimming, no helper text)
  • Clicking triggers a friendly error on submit instead of being blocked up-front

The mismatch between cascade behaviour (functionally disabled) and visual treatment (looks enabled) breaks the user's mental model.

Expected

BUY CHARM should match the visual disabled treatment used by the other post-end CTAs when chainMismatch === true:

  • Dimmed / muted button styling
  • Tooltip or helper text explaining the wrong-chain state
  • Same preventative UX pattern as End sale / Redeem charms / Distribute prizes

Relevant docs / skills / files

Apply the same visual disabled treatment (muted styling + helper title) that End sale uses, gated on the existing chainMismatch boolean already passed to the BUY CHARM button. No new state needed — the cascade already includes chainMismatch; only the styling layer needs alignment.

Acceptance criteria

  • BUY CHARM renders visually disabled (matching End sale styling) when chainMismatch === true
  • Helper title / tooltip explains the wrong-chain reason
  • Switching back to chain 31337 restores normal active styling reactively (no refresh)
  • Existing friendly-revert error path is preserved as a defence-in-depth fallback

Verification checklist

  • Connect wallet, switch MetaMask to any non-31337 chain — BUY CHARM dims with helper text
  • Switch back to 31337 — BUY CHARM re-enables without page refresh
  • All other post-end CTAs continue to behave as before
## Summary During local-stack visual QA ([#164](https://gitlab.com/PlasticDigits/yieldomega/-/issues/164) §5 item 4), the **BUY CHARM** button on Arena renders visually as **enabled** even when the connected wallet is on the wrong chain. Clicking surfaces the friendly `Wallet or network changed during purchase — please retry from the beginning` revert at submit time — so the contract-level protection works (no rogue tx), but the disabled state is **reactive** rather than **preventative** in the UI. Other Arena post-end CTAs (End sale / Redeem charms / Distribute prizes at `TimeCurveArenaView.tsx:1168/1177/1186`) properly disable visually on `chainMismatch`. The BUY CHARM button at `TimeCurveArenaView.tsx:861` already includes `chainMismatch` in its disabled-prop cascade (line 866) per source — the gap is purely visual / disabled-state styling. **P3 polish** — preventative UX consistency, not a security issue. ## What is wrong On `/timecurve/arena`, with wallet connected to a chain other than 31337: - End sale / Redeem charms / Distribute prizes correctly render visually disabled with helper title - BUY CHARM renders visually as enabled (no dimming, no helper text) - Clicking triggers a friendly error on submit instead of being blocked up-front The mismatch between cascade behaviour (functionally disabled) and visual treatment (looks enabled) breaks the user's mental model. ## Expected BUY CHARM should match the visual disabled treatment used by the other post-end CTAs when `chainMismatch === true`: - Dimmed / muted button styling - Tooltip or helper text explaining the wrong-chain state - Same preventative UX pattern as End sale / Redeem charms / Distribute prizes ## Relevant docs / skills / files - Umbrella: [#164](https://gitlab.com/PlasticDigits/yieldomega/-/issues/164) §5 item 4 - Source: [`frontend/src/pages/timeCurveArena/TimeCurveArenaView.tsx`](frontend/src/pages/timeCurveArena/TimeCurveArenaView.tsx) line 861 (BUY CHARM disabled cascade) — reference visual treatment at lines 1168/1177/1186 (other CTAs) - Invariants: [`docs/testing/invariants-and-business-logic.md`](docs/testing/invariants-and-business-logic.md) (Arena chain-mismatch surfaces) ## Recommended fix Apply the same visual disabled treatment (muted styling + helper title) that End sale uses, gated on the existing `chainMismatch` boolean already passed to the BUY CHARM button. No new state needed — the cascade already includes `chainMismatch`; only the styling layer needs alignment. ## Acceptance criteria - [ ] BUY CHARM renders visually disabled (matching End sale styling) when `chainMismatch === true` - [ ] Helper title / tooltip explains the wrong-chain reason - [ ] Switching back to chain 31337 restores normal active styling reactively (no refresh) - [ ] Existing friendly-revert error path is preserved as a defence-in-depth fallback ## Verification checklist - [ ] Connect wallet, switch MetaMask to any non-31337 chain — BUY CHARM dims with helper text - [ ] Switch back to 31337 — BUY CHARM re-enables without page refresh - [ ] All other post-end CTAs continue to behave as before
Brouie commented 2026-05-11 09:17:03 +00:00 (Migrated from gitlab.com)

mentioned in issue #164

mentioned in issue #164
PlasticDigits commented 2026-05-11 10:33:44 +00:00 (Migrated from gitlab.com)

mentioned in commit 299756ca0f0ddb6ca11f8d902bab66accc49cf68

mentioned in commit 299756ca0f0ddb6ca11f8d902bab66accc49cf68
PlasticDigits commented 2026-05-11 10:34:01 +00:00 (Migrated from gitlab.com)

Implemented (merged to main, commit 299756c)

Summary: Arena Buy CHARM now matches preventative wrong-network UX from #95: the arcade CTA is visually muted when useWalletTargetChainMismatch() is true, exposes the same copy as chainMismatchWriteMessage via native title, skips Framer hover/tap lift so it does not read as interactable, and the buy-panel ChainMismatchWriteBarrier overlay uses a higher z-index so it stacks above z-indexed panel chrome. disabled cascade and handleBuy preflights are unchanged (defence in depth).

Code / docs: TimeCurveArenaView.tsx (data-testid="timecurve-arena-buy-charm-cta"), index.css, INV-FRONTEND-194-ARENA-BUY-CHAIN in docs/testing/invariants-and-business-logic.md, docs/frontend/timecurve-views.md §194, docs/testing/manual-qa-checklists.md §194, skills/README.md, .cursor/skills/yieldomega-guardrails/SKILL.md (item 29).

Tests run: frontend/ npm run typecheck + npm test (Vitest) — all green.


Verification checklist (for QA)

  • Connect wallet on wrong chain vs build VITE_CHAIN_ID (default local 31337): /timecurve/arena Buy CHARM is dimmed (not the same gold “live lever” as on the correct chain), disabled, and hovering shows wrong-network copy in the native tooltip (title).
  • data-testid="timecurve-arena-buy-charm-cta" is present on that control.
  • Switch network back to the build target without full page reload: CTA returns to normal arcade styling and hover motion (unless reduced motion).
  • End sale / Redeem charms / Distribute prizes wrong-chain behaviour unchanged vs prior main.
  • Optional: confirm timecurve-arena-buy-chain-write-gate overlay still appears and remains clickable above the dimmed panel.

@brouie Could you confirm on your stack (per #164 visual QA) that the mental model now matches the post-end CTAs? Leaving this issue open until you sign off.

## Implemented (merged to `main`, commit 299756c) **Summary:** Arena **Buy CHARM** now matches preventative wrong-network UX from [#95](https://gitlab.com/PlasticDigits/yieldomega/-/issues/95): the arcade CTA is **visually muted** when `useWalletTargetChainMismatch()` is true, exposes the same copy as `chainMismatchWriteMessage` via native **`title`**, skips Framer hover/tap lift so it does not read as interactable, and the buy-panel `ChainMismatchWriteBarrier` overlay uses a **higher z-index** so it stacks above z-indexed panel chrome. `disabled` cascade and `handleBuy` preflights are unchanged (defence in depth). **Code / docs:** `TimeCurveArenaView.tsx` (`data-testid="timecurve-arena-buy-charm-cta"`), `index.css`, **`INV-FRONTEND-194-ARENA-BUY-CHAIN`** in `docs/testing/invariants-and-business-logic.md`, `docs/frontend/timecurve-views.md` §194, `docs/testing/manual-qa-checklists.md` §194, `skills/README.md`, `.cursor/skills/yieldomega-guardrails/SKILL.md` (item 29). **Tests run:** `frontend/` `npm run typecheck` + `npm test` (Vitest) — all green. --- ### Verification checklist (for QA) - [ ] Connect wallet on **wrong** chain vs build `VITE_CHAIN_ID` (default local **31337**): `/timecurve/arena` **Buy CHARM** is **dimmed** (not the same gold “live lever” as on the correct chain), **disabled**, and hovering shows **wrong-network** copy in the native tooltip (`title`). - [ ] `data-testid="timecurve-arena-buy-charm-cta"` is present on that control. - [ ] **Switch network** back to the build target **without** full page reload: CTA returns to normal arcade styling and hover motion (unless reduced motion). - [ ] **End sale / Redeem charms / Distribute prizes** wrong-chain behaviour unchanged vs prior `main`. - [ ] Optional: confirm `timecurve-arena-buy-chain-write-gate` overlay still appears and remains clickable above the dimmed panel. --- @brouie Could you confirm on your stack (per [#164](https://gitlab.com/PlasticDigits/yieldomega/-/issues/164) visual QA) that the mental model now matches the post-end CTAs? Leaving this issue **open** until you sign off.
Brouie commented 2026-05-11 11:42:05 +00:00 (Migrated from gitlab.com)

verified at HEAD 5ad24ba — full pass on all 5 checklist items.

source spot-checks:

  • new CSS rule .timecurve-simple__cta--arcade.timecurve-simple__cta--wrong-network:disabled at index.css applies the visible disabled treatment (opacity 0.38, grayscale 0.82, reduced box-shadow, text-shadow none) plus z-index 40 on .chain-write-gate__overlay so the #95 barrier stacks above panel chrome
  • TimeCurveArenaView.tsx BUY CHARM button: conditional timecurve-simple__cta--wrong-network class on chainMismatch, native title={chainMismatchWriteMessage(walletChainId)} exposing the same copy as End sale family, framer-motion props skipped ({...(chainMismatch ? {} : primaryButtonMotion)}) so the button does not lift/bounce on hover when mismatched, data-testid="timecurve-arena-buy-charm-cta" added
  • disabled cascade and handleBuy preflights unchanged (defence in depth preserved)

tests:

  • full vitest 316/316 pass — no regressions (was 313 pre-fix, +3 from #195 new tests)

live walk on fresh stack at HEAD (saleActive, swarm running):

  • wrong chain (wallet on BNB Chain, build target 31337): BUY CHARM dimmed + desaturated + reduced lift, header shows Wrong Network chip, disabled cascade fires, hover surfaces wrong-network copy via native tooltip
  • data-testid="timecurve-arena-buy-charm-cta" confirmed present in DOM
  • switch back to chain 31337 without page reload: BUY CHARM returns to normal gold arcade styling immediately, hover lift/bounce motion restored
  • End sale / Redeem charms / Distribute prizes wrong-chain behaviour unchanged from prior main
  • timecurve-arena-buy-chain-write-gate overlay confirmed visible and clickable above the dimmed panel on wrong chain
  • mental model now matches the post-end CTAs as requested

ready for close on your side @PlasticDigits

verified at HEAD 5ad24ba — full pass on all 5 checklist items. source spot-checks: - new CSS rule `.timecurve-simple__cta--arcade.timecurve-simple__cta--wrong-network:disabled` at index.css applies the visible disabled treatment (opacity 0.38, grayscale 0.82, reduced box-shadow, text-shadow none) plus z-index 40 on `.chain-write-gate__overlay` so the #95 barrier stacks above panel chrome - TimeCurveArenaView.tsx BUY CHARM button: conditional `timecurve-simple__cta--wrong-network` class on `chainMismatch`, native `title={chainMismatchWriteMessage(walletChainId)}` exposing the same copy as End sale family, framer-motion props skipped (`{...(chainMismatch ? {} : primaryButtonMotion)}`) so the button does not lift/bounce on hover when mismatched, `data-testid="timecurve-arena-buy-charm-cta"` added - `disabled` cascade and `handleBuy` preflights unchanged (defence in depth preserved) tests: - full vitest 316/316 pass — no regressions (was 313 pre-fix, +3 from #195 new tests) live walk on fresh stack at HEAD (saleActive, swarm running): - wrong chain (wallet on BNB Chain, build target 31337): BUY CHARM dimmed + desaturated + reduced lift, header shows `Wrong Network` chip, disabled cascade fires, hover surfaces wrong-network copy via native tooltip - `data-testid="timecurve-arena-buy-charm-cta"` confirmed present in DOM - switch back to chain 31337 without page reload: BUY CHARM returns to normal gold arcade styling immediately, hover lift/bounce motion restored - End sale / Redeem charms / Distribute prizes wrong-chain behaviour unchanged from prior main - `timecurve-arena-buy-chain-write-gate` overlay confirmed visible and clickable above the dimmed panel on wrong chain - mental model now matches the post-end CTAs as requested ready for close on your side @PlasticDigits
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-12 07:25:58 +00:00
PlasticDigits commented 2026-05-13 15:13:34 +00:00 (Migrated from gitlab.com)

mentioned in commit bd744504d2

mentioned in commit bd744504d217ad5f27d89bba00804eced431f1bb
PlasticDigits commented 2026-06-03 13:55:06 +00:00 (Migrated from gitlab.com)

mentioned in merge request !417

mentioned in merge request !417
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#194
No description provided.