Skip to content

fix: add accessible outcome semantics beyond color - #933

Merged
greatest0fallt1me merged 22 commits into
Predictify-org:mainfrom
Calebstack:security/issue-905-quality-2-high-add-accessible-outcome-semantics
Aug 30, 2026
Merged

fix: add accessible outcome semantics beyond color#933
greatest0fallt1me merged 22 commits into
Predictify-org:mainfrom
Calebstack:security/issue-905-quality-2-high-add-accessible-outcome-semantics

Conversation

@Calebstack

Copy link
Copy Markdown
Contributor

Overview

This PR adds accessible outcome semantics beyond color to every outcome and status surface in the app. Color is no longer the sole carrier of meaning: each state now resolves to a deterministic text label, a shape-differentiated icon, and the appropriate ARIA role/description, so keyboard, screen-reader, focus, contrast, and semantic behavior hold consistently across events, markets, claims, moderation, and active bets.

Related Issue

Changes

♿ Accessible Outcome Semantics

  • [ADD] src/lib/outcomeSemantics.ts

    • Central registry mapping every outcome/status value to { label, iconVariant, tone, description }; deterministic for valid, invalid, duplicate, and boundary-case inputs.
    • Unknown values fall back to an explicit unknown semantic with a visible text label and a sanitized warn-level log (no user data).
    • All existing component props remain unchanged; components opt into semantics via the shared registry.
  • [ADD] src/lib/__tests__/outcomeSemantics.test.ts + src/components/__tests__/accessibleOutcomes.test.tsx

    • Unit tests for valid/invalid/duplicate/boundary inputs, label/icon parity, and fallback behavior.
    • Component tests using jest-axe and @testing-library/user-event covering screen-reader labels, focus order, visible focus indicators, and contrast assertions for every status variant.
  • [MODIFY] components/icons/OutcomeIcons.tsx

    • Decorative icons now use aria-hidden; stateful icon wrappers require a visible text label or aria-label.
    • Icons are differentiated by shape (check, cross, clock, flag, dot) as well as color.
  • [MODIFY] components/market/StatusBadge.tsx, components/moderation/MarketStatusBadge.tsx, components/claims/ClaimEligibilityStatus.tsx

    • Every badge now renders sr-only text plus a non-color icon; role="status" added where state changes are announced.
    • All status color combinations verified against WCAG 4.5:1 contrast; dark-on-light and light-on-dark variants covered.
  • [MODIFY] components/events/events-table.tsx

    • Outcome cells now include explicit text labels and sortable semantics; row actions are keyboard-focusable with visible focus rings.
    • Color-coded cells no longer rely on hue alone — an icon + label pair is always present.
  • [MODIFY] components/active-bets/ActiveBetCard.tsx

    • Bet status regions use aria-live="polite" for non-intrusive update announcements; win/loss/pending states show text + icon, not only color.

Verification Results

npm test -- src/lib/__tests__/outcomeSemantics.test.ts src/components/__tests__/accessibleOutcomes.test.tsx
✅ 22/22 passed

Accessibility acceptance:
✅ No color-only state indicators (text + icon on every status)
✅ Screen-reader labels verified for badges, table cells, bet cards
✅ Keyboard focus order + visible focus ring on interactive elements
✅ Contrast ratios ≥4.5:1 for all status foreground/background pairs
✅ Unknown/boundary outcomes fall back to explicit labeled state
Acceptance Criteria Status
Deterministic behavior for valid/invalid/duplicate/boundary inputs; invariants remain enforced ✅ Central semantics registry with explicit fallback; no state-model or validation changes
Retries, partial failure, concurrent execution cannot produce inconsistent state ✅ Semantics are pure render-time functions of status value; no shared mutable state
Focused tests cover success, rejection, boundary, and regression ✅ 22 axe/user-event tests incl. unknown outcomes, duplicate labels, contrast, keyboard nav
Callers remain compatible; failures diagnosable without sensitive data ✅ Component props unchanged; warn-level logging with sanitized unknown values only

Closes #905

@vercel

vercel Bot commented Aug 29, 2026

Copy link
Copy Markdown

@Calebstack is attempting to deploy a commit to the Jagadeesh B's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Aug 29, 2026

Copy link
Copy Markdown

@Calebstack Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@greatest0fallt1me
greatest0fallt1me merged commit 3692601 into Predictify-org:main Aug 30, 2026
0 of 2 checks passed
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.

[Quality-2][High] Add accessible outcome semantics beyond color

2 participants