Skip to content

Error and warning Alert boxes across Deposit/Unlock/PoolDetail hardcode hex colors instead of theme tokens — breaks contrast and theming in light mode #141

Description

@prodbycorne

Overview

src/lib/theme.ts defines a complete semanticTokens.colors set (app.bg, app.surface, app.text, app.accent, etc.) with explicit default/_dark pairs, including a code comment documenting a deliberate contrast audit: "app.accent": { default: "#0f7a4e", _dark: "#4ae292" }, // Accent — #0f7a4e passes 4.5:1 on white; #4ae292 is the dark-mode green. This is the mechanism #44 (closed, "Add WCAG-compliant dark/light theme toggle with colour-contrast audit on all text elements") established. There is, however, no semantic token for error or warning states anywhere in theme.ts — no app.error, app.errorBg, app.warning, app.warningBg. Every error/warning Alert across the deposit and unlock flows instead hardcodes literal, dark-mode-only hex values directly in JSX, completely bypassing the token system:

// src/app/farm/page.tsx — DepositModal, three separate hardcoded pairs
<Alert bg="#2a1414" color="#ff8080">...</Alert>                          {/* error */}
<Alert bg="#2d2216" color="#ffb86c" border="1px solid #7c5c24">...</Alert> {/* fee-sponsored warning */}
<Alert bg="#2a2412" color="#f6c453">...</Alert>                          {/* not-connected warning */}

// src/components/UnlockModal/UnlockModal.tsx — same pattern repeated
<Alert bg="#2a2412" color="#f6c453">...</Alert>                          {/* lock-period warning */}
<Alert bg="#2a1414" color="#ff8080">...</Alert>                          {/* error */}

theme.ts's initialColorMode: "dark" is only the defaultThemeToggle.tsx (useColorMode().toggleColorMode) lets any user switch to light mode, persisted via colorModeManager={localStorageManager} (context/index.tsx:35), and every other color in the app correctly re-themes because it references an app.* token. These hardcoded alert colors do not: #2a1414 is a near-black, low-luminance dark red designed to sit against a dark surface with #ff8080 (a light salmon) as legible text on top of it. Once a user switches to light mode, this exact same near-black box with light-pink text renders unchanged — a jarring, un-themed dark patch inside an otherwise white/light-gray page, with no guarantee the resulting contrast ratio (dark-red-on-white-page-adjacent, light-pink-text-on-dark-red-box) was ever audited the way app.accent's comment documents #0f7a4e/#4ae292 were.

Requirements

  • Add semantic tokens to theme.ts for error and warning states (e.g. app.errorBg/app.errorFg, app.warningBg/app.warningFg), each with a real, documented default/_dark pair audited for contrast the same way the existing accent/text tokens were.
  • Replace every hardcoded hex literal in DepositModal (farm/page.tsx), UnlockModal.tsx, and PoolDetailClient.tsx's error/warning Alerts with the new semantic tokens.
  • Verify visually (or via an automated contrast-ratio check) that both light and dark mode meet WCAG AA (4.5:1) for these alert boxes, matching the rigor of the existing accent-color comment.

Acceptance Criteria

  • theme.ts defines app.errorBg/app.errorFg and app.warningBg/app.warningFg (naming flexible) with both default and _dark values.
  • No component under src/app/farm, src/app/farm/[poolId], or src/components/UnlockModal hardcodes a literal hex color for an error/warning Alert's bg/color/border props — all reference the new tokens.
  • Toggling to light mode via ThemeToggle changes the rendered background/text color of these alerts (verifiable via a snapshot or computed-style test across both color modes).
  • Contrast ratios for both light and dark variants meet WCAG AA (4.5:1 for normal text), documented the same way app.accent's comment documents its own ratio.

Additional Notes

More precise references

  • src/lib/theme.ts:14-34 (semanticTokens.colors) — confirmed the full token list and confirmed no error/warning-flavored token exists anywhere in it.
  • src/lib/theme.ts:24 — confirmed the exact contrast-audit comment style ("#0f7a4e passes 4.5:1 on white") that error/warning tokens should match once added.
  • src/app/farm/page.tsx:355-360 (amountValid error text, color="#ff8080"), :362-367 (exceedsBalance, same), :369-374 (localError, same), :376-381 (isFeeSponsored, bg="#2d2216" color="#ffb86c" border="1px solid #7c5c24"), :383-388 (not-connected, bg="#2a2412" color="#f6c453") — confirmed five separate hardcoded-color Alerts in this one component alone.
  • src/components/UnlockModal/UnlockModal.tsx:312-323 (lock-period warning, bg="#2a2412" color="#f6c453"), :412-424 (timeout warning, same pair), :426-440 (min-deposit warning, same pair again), :442-453 (error, bg="#2a1414" color="#ff8080") — confirmed the identical color pairs are reused across at least four separate alert instances in this file too, consistent with the pattern being systemic rather than a one-off typo.
  • src/lib/theme.ts:3-6 (config.initialColorMode: "dark", useSystemColorMode: false) and src/components/ThemeToggle/ThemeToggle.tsx:27-30 (useColorMode().toggleColorMode) — confirmed light mode is a real, user-reachable state, not merely a theoretical config option.
  • src/context/index.tsx:35 (colorModeManager={localStorageManager}) — confirmed a user's light-mode choice persists across sessions, so this isn't a transient state a user would rarely encounter.

Additional edge cases

  • The exact same #2a2412/#f6c453 pair (warning) and #2a1414/#ff8080 pair (error) recur verbatim across both files, confirming these were likely copy-pasted from one original, dark-mode-only design rather than independently chosen — a single pair of new semantic tokens can replace all of them in one pass.
  • PoolDetailClient.tsx's deposit modal reuses the same error-color pattern for its own error Alert (flow.step === "error" branch) — confirm it's included in the same sweep, not just the two files explicitly quoted above.
  • Because these hardcoded colors currently happen to look acceptable in the app's default dark mode, this bug has zero visible symptoms for the overwhelming majority of users/reviewers who never toggle to light mode — worth explicitly testing in light mode as part of any manual QA pass for this fix, not just relying on the default view.

Implementation sketch

// theme.ts, added to semanticTokens.colors:
"app.errorBg":   { default: "#fdecec", _dark: "#2a1414" },
"app.errorFg":   { default: "#b3261e", _dark: "#ff8080" },
"app.warningBg": { default: "#fdf3e2", _dark: "#2a2412" },
"app.warningFg": { default: "#8a5a00", _dark: "#f6c453" },
// farm/page.tsx / UnlockModal.tsx, e.g.:
<Alert status="error" borderRadius="2xl" bg="app.errorBg" color="app.errorFg">

(Exact hex values above are illustrative starting points, not final — a real contrast check against both the light app.bg (#ffffff) and dark app.bg (#0b0d0c) should confirm/adjust them before merging, mirroring how app.accent's existing values were justified.)

Test/reproduction plan

  • Render DepositModal with an invalid amount (triggering the error Alert) under both ChakraProvider color modes; assert the computed background/text color differs between light and dark (proving it's token-driven, not hardcoded) and that each meets a 4.5:1 contrast check (e.g. via a small contrast-ratio utility in the test).
  • Visual/manual QA: toggle ThemeToggle while an error or warning alert is visible in UnlockModal/DepositModal; confirm it re-themes instead of staying frozen.

Cross-references

Metadata

Metadata

Assignees

No one assigned

    Labels

    GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial CampaignCampaign: Official CampaignOfficial Campaign | FWC26Campaign: Official Campaign | FWC26Third CampaignCampaign: Third CampaignaccessibilityWCAG / keyboard / screen reader complianceuxUser experience, interaction design, loading statesvery hardExtremely hard — deep expertise, careful design, and significant time required

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions