Skip to content

fix(ui): add accessible operational colors - #2226

Merged
Asherlc merged 4 commits into
mainfrom
codex/issue-2068
Jul 28, 2026
Merged

Asherlc merged 4 commits into
mainfrom
codex/issue-2068

Conversation

@Asherlc

@Asherlc Asherlc commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • add a public light-theme operationalStatusColors palette with paired foreground, surface, border, and indicator roles while preserving every existing color alias
  • map generic web and mobile query errors, processing states, recompute progress, provider sync states, and the web error fallback to the shared roles
  • keep state understandable without color through visible labels and symbols, alert semantics, retry actions, rendered contrast assertions, and Storybook variants

TDD plan

  • docs/superpowers/plans/2026-07-27-accessible-operational-colors.md

Validation

  • pnpm test — 888 files passed, 2 skipped; 14,106 tests passed, 21 skipped
  • pnpm test:changed on the exact combined head — 211 files / 2,873 tests passed
  • root, server, web, and mobile pnpm tsc --noEmit — passed
  • Biome — 2,426 files passed; analytics policy, mobile telemetry policy, and web Storybook coverage passed
  • Full local pnpm lint reached SQLFluff, then stopped because the instructed no-Docker environment has no ClickHouse listener at 127.0.0.1:8123; exact-head CI owns that container-backed gate

Fixes #2068

Summary by Sourcery

Introduce a shared light-theme operational status palette and apply it across web and mobile status presenters for accessible, non-color-only state feedback.

New Features:

  • Add a public operationalStatusColors palette for neutral/info/success/warning/danger operational states with foreground, surface, border, and indicator roles.
  • Expose the shared operational status palette through the mobile theme and Storybook stories for processing and recompute indicators.

Bug Fixes:

  • Fix low-contrast query error and fallback panels by using accessible foreground/surface/border combinations and verifying WCAG contrast ratios in tests.
  • Ensure error and sync states are announced to assistive technologies and are distinguishable without relying solely on color, including explicit symbols and labels.
  • Correct provider sync, processing, and recompute indicators to use consistent, accessible tones across web and mobile.

Enhancements:

  • Add accessible retry actions and alert semantics to query error panels and error boundaries on web and mobile.
  • Unify provider sync status and history visuals with shared operational roles, adding visible status labels and symbols.
  • Align processing and recompute status rings and progress bars with the shared operational status palette on all platforms.
  • Document the accessible operational color strategy and test plan in the scoring package and superpowers docs.

Tests:

  • Extend and add tests to assert contrast ratios, ARIA roles, and non-color cues for query error panels, status dots, error boundaries, and processing/recompute presenters.
  • Add Storybook stories for mobile SourceProcessingStatusCard and RecomputeStatusIndicator variants to visualize the new operational states.

Summary by cubic

Add a shared light-theme palette operationalStatusColors and apply it across web and mobile error, processing, recompute, and sync UI. Fixes #2068 by meeting WCAG contrast and replacing color-only indicators with visible labels, symbols, and retry actions.

  • New Features

    • Export operationalStatusColors (neutral/info/success/warning/danger with foreground/surface/border/indicator) from @dofek/scoring/colors and re-export via mobile theme.
    • Map web/mobile QueryStatePanel (adds optional contextLabel), ErrorBoundary, RecomputeStatusIndicator, SourceProcessingStatusCard, StatusDot, and provider sync UI to the shared roles while keeping the existing statusColors API intact.
  • Bug Fixes

    • Meet WCAG 2.2 contrast; add alert semantics/live regions; use contextual error headings to avoid nested alerts in Daily Overview and in the error boundary fallback.
    • Replace color-only indicators with visible labels and symbols; add accessible retry actions and disabled states.
    • Unify rings/dots and sync history with labeled ✓/! chips; add tests and stories for contrast, role mapping, announcements, contextual labels, and history markers across web and mobile.

Written for commit eb79842. Summary will update on new commits.

Review in cubic

Copilot AI review requested due to automatic review settings July 27, 2026 23:32
@Asherlc Asherlc linked an issue Jul 27, 2026 that may be closed by this pull request
@cursor

cursor Bot commented Jul 27, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@Asherlc, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 21 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2b152288-20a7-4187-911c-227176b5a73b

📥 Commits

Reviewing files that changed from the base of the PR and between fa283fe and eb79842.

📒 Files selected for processing (29)
  • docs/superpowers/plans/2026-07-27-accessible-operational-colors.md
  • packages/mobile/components/QueryStatePanel.stories.tsx
  • packages/mobile/components/QueryStatePanel.test.tsx
  • packages/mobile/components/QueryStatePanel.tsx
  • packages/mobile/components/RecomputeStatusIndicator.stories.tsx
  • packages/mobile/components/RecomputeStatusIndicator.test.tsx
  • packages/mobile/components/RecomputeStatusIndicator.tsx
  • packages/mobile/components/SourceProcessingStatusCard.stories.tsx
  • packages/mobile/components/SourceProcessingStatusCard.test.tsx
  • packages/mobile/components/SourceProcessingStatusCard.tsx
  • packages/mobile/theme.ts
  • packages/scoring/README.md
  • packages/scoring/src/colors.ts
  • packages/web/src/components/DailyOverview.test.tsx
  • packages/web/src/components/DailyOverview.tsx
  • packages/web/src/components/ErrorBoundary.test.tsx
  • packages/web/src/components/ErrorBoundary.tsx
  • packages/web/src/components/QueryStatePanel.stories.tsx
  • packages/web/src/components/QueryStatePanel.test.tsx
  • packages/web/src/components/QueryStatePanel.tsx
  • packages/web/src/components/RecomputeStatusIndicator.test.tsx
  • packages/web/src/components/RecomputeStatusIndicator.tsx
  • packages/web/src/components/SourceProcessingStatusCard.test.tsx
  • packages/web/src/components/SourceProcessingStatusCard.tsx
  • packages/web/src/components/StatusDot.test.tsx
  • packages/web/src/components/StatusDot.tsx
  • packages/web/src/components/SyncProviderCard.test.tsx
  • packages/web/src/components/SyncProviderCard.tsx
  • packages/web/src/index.css

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @Asherlc, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@sourcery-ai

sourcery-ai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Introduces a shared light-theme operationalStatusColors palette and applies it across web and mobile operational UI (query panels, sync/provider status, processing/recompute indicators, and error fallback) to meet WCAG contrast and accessibility expectations while preserving existing status color APIs.

File-Level Changes

Change Details Files
Add shared accessible operational status color palette and expose it to mobile theme consumers
  • Introduce operationalStatusColors with neutral/info/success/warning/danger roles including foreground, surface, border, and indicator tokens tuned for WCAG contrast
  • Document operationalStatusColors usage and guarantees in the scoring README
  • Re-export operationalStatusColors from the mobile theme module alongside existing color token groups
packages/scoring/src/colors.ts
packages/scoring/README.md
packages/mobile/theme.ts
Refactor web and mobile query error panels to use shared danger tone, explicit alert semantics, and color-independent cues
  • Web QueryStatePanel now applies operationalStatusColors.danger for surface, border, foreground and adds icon, heading, and alert role; retry button visually matches error tone
  • Mobile QueryStatePanel now uses operationalStatusColors.danger for panel, text, icon, and retry button styling; adds accessible retry control with disabled state and improved accessibility roles
  • Add contrast-ratio helpers and tests asserting WCAG contrast, alert roles, icon presence, retry behavior, and accessible labels on both platforms
packages/web/src/components/QueryStatePanel.tsx
packages/web/src/components/QueryStatePanel.test.tsx
packages/web/src/index.css
packages/mobile/components/QueryStatePanel.tsx
packages/mobile/components/QueryStatePanel.test.tsx
packages/mobile/components/QueryStatePanel.stories.tsx
Unify provider sync and processing/recompute status presentations around the shared operational palette with visible labels and symbols
  • Update StatusDot to render a labeled pill with symbol and operationalStatusColors tones instead of a color-only dot; add tests including contrast checks
  • Adjust SyncProviderCard provider badges and recent sync history indicators to use operationalStatusColors, visible labels, and accessible descriptions instead of raw Tailwind colors
  • Wire SourceProcessingStatusCard (web and mobile) and RecomputeStatusIndicator (web and mobile) to operationalStatusColors indicators for borders, progress bars, and rings; add tests verifying mappings and indicator colors
  • Add Storybook stories for mobile SourceProcessingStatusCard and RecomputeStatusIndicator states
packages/web/src/components/StatusDot.tsx
packages/web/src/components/StatusDot.test.tsx
packages/web/src/components/SyncProviderCard.tsx
packages/web/src/components/SyncProviderCard.test.tsx
packages/web/src/components/SourceProcessingStatusCard.tsx
packages/web/src/components/SourceProcessingStatusCard.test.tsx
packages/web/src/components/RecomputeStatusIndicator.tsx
packages/web/src/components/RecomputeStatusIndicator.test.tsx
packages/mobile/components/SourceProcessingStatusCard.tsx
packages/mobile/components/SourceProcessingStatusCard.test.tsx
packages/mobile/components/SourceProcessingStatusCard.stories.tsx
packages/mobile/components/RecomputeStatusIndicator.tsx
packages/mobile/components/RecomputeStatusIndicator.test.tsx
packages/mobile/components/RecomputeStatusIndicator.stories.tsx
Make the web error boundary use the shared error tone, accessible alert semantics, and retry styling
  • ErrorBoundary fallback now uses operationalStatusColors.danger for background, border, and text plus a visible icon and heading with role="alert"
  • Retry button is styled with the same error tone and explicit border while preserving behavior
  • Add focused tests for announced error fallback, icon/heading, color usage, and retry handler invocation
packages/web/src/components/ErrorBoundary.tsx
packages/web/src/components/ErrorBoundary.test.tsx
Add TDD plan capturing accessibility goals, scope, and validation strategy for operational colors
  • Create a superpowers TDD plan describing goals, standards, affected components, and task breakdown for the accessible operational colors work
docs/superpowers/plans/2026-07-27-accessible-operational-colors.md

Assessment against linked issues

Issue Objective Addressed Explanation
#2068 Introduce shared light-theme operational semantic color tokens (neutral/info/success/warning/danger with foreground, surface, border, indicator roles) that meet WCAG 2.2 AA contrast requirements, and document their usage without breaking existing status color APIs.
#2068 Update web and mobile error and operational UI (query error panels, sync/provider status indicators, processing and recompute presenters, and the web error fallback) to use the shared operational tokens so that normal text reaches at least 4.5:1 contrast and non-text elements (borders, indicators) reach at least 3:1, with tests asserting these contrasts.
#2068 Ensure that operational and error states are understandable without relying on color alone by adding icons, visible labels, accessible roles/announcements, and retry actions across the affected components.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add WCAG-compliant operational status colors across web and mobile UI

🐞 Bug fix ✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Introduce a shared light-theme operational status palette with WCAG contrast targets.
• Migrate web/mobile error, processing, recompute, and sync UI to shared color roles.
• Add tests and Storybook variants asserting contrast, labels/symbols, alerts, and retries.
Diagram

graph TD
  TOK["operationalStatusColors (scoring)"] --> WEB["Web operational UI"] --> WEBT[["Web a11y/contrast tests"]]
  TOK --> MOB["Mobile operational UI"] --> MOBT[["Mobile a11y/contrast tests & stories"]]
  TOK --> DOC[/"TDD plan & docs"/]
  subgraph Legend
    direction LR
    _tok["Shared tokens"] ~~~ _ui["UI components"] ~~~ _t[["Tests/Stories"]] ~~~ _d[/"Docs"/]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Reuse existing domain-oriented statusColors everywhere
  • ➕ No new public export surface area
  • ➕ Less token duplication
  • ➖ Blurs domain meaning (health/training/scoring) with generic operational state
  • ➖ Harder to guarantee WCAG contrast without affecting existing semantics
2. Define operational roles as web CSS variables + map to RN theme
  • ➕ Leverages theming infrastructure on the web
  • ➕ Easier runtime theming if dark theme is added later
  • ➖ Introduces indirection and potential drift between platforms
  • ➖ Still requires a canonical source of truth for RN tokens
3. Adopt a centralized contrast utility and snapshot rendered colors only
  • ➕ Reduces repeated luminance/contrast helpers across test files
  • ➕ Encourages consistent contrast assertions
  • ➖ Requires new shared test helper package/module wiring
  • ➖ Potentially overkill for a small number of suites

Recommendation: Keep the PR’s approach: a dedicated operational palette is the cleanest separation of concerns (generic app state vs domain meaning) and makes WCAG targets explicit. Consider a follow-up to DRY the repeated contrast helper functions into a shared test utility once these patterns stabilize.

Files changed (27) +848 / -97

Enhancement (6) +101 / -38
RecomputeStatusIndicator.tsxMap recompute statuses to operational indicator roles (mobile) +8/-5

Map recompute statuses to operational indicator roles (mobile)

• Replaces theme accent/warning/negative colors with operationalStatusColors-based indicators. Ensures consistent meaning and presentation across platforms.

packages/mobile/components/RecomputeStatusIndicator.tsx

SourceProcessingStatusCard.tsxUse operational indicator roles for processing card accents (mobile) +10/-9

Use operational indicator roles for processing card accents (mobile)

• Updates progress fill and per-status border-left colors to operationalStatusColors indicators. Aligns mobile processing visuals with shared operational semantics.

packages/mobile/components/SourceProcessingStatusCard.tsx

theme.tsRe-export operationalStatusColors from mobile theme module +8/-2

Re-export operationalStatusColors from mobile theme module

• Extends the mobile theme re-exports to include operationalStatusColors. Makes the shared palette available to mobile components through the existing theme entrypoint.

packages/mobile/theme.ts

colors.tsAdd operationalStatusColors palette with role-based tokens +41/-0

Add operationalStatusColors palette with role-based tokens

• Introduces a new exported palette (neutral/info/success/warning/danger) with foreground/surface/border/indicator roles. Comments define WCAG AA text contrast and 3:1 non-text contrast expectations.

packages/scoring/src/colors.ts

RecomputeStatusIndicator.tsxReplace Tailwind status classes with operational indicator colors (web) +15/-11

Replace Tailwind status classes with operational indicator colors (web)

• Switches from Tailwind text color classes to a status→indicator color map based on operationalStatusColors. Applies color via inline style to both spinner and SVG ring paths.

packages/web/src/components/RecomputeStatusIndicator.tsx

SourceProcessingStatusCard.tsxUse operational indicator roles for processing card border and progress fill (web) +19/-11

Use operational indicator roles for processing card border and progress fill (web)

• Replaces Tailwind border color classes with inline borderLeftColor driven by operationalStatusColors. Updates progress fill to use the shared info indicator color.

packages/web/src/components/SourceProcessingStatusCard.tsx

Bug fix (6) +210 / -43
QueryStatePanel.tsxUse operational danger roles and add accessible retry UX (mobile) +67/-6

Use operational danger roles and add accessible retry UX (mobile)

• Switches error styling to operationalStatusColors.danger with explicit border and foreground. Adds alert semantics, a visible error symbol, and an accessible retry button with disabled state handling.

packages/mobile/components/QueryStatePanel.tsx

ErrorBoundary.tsxUse operational danger roles for ErrorBoundary fallback styling (web) +30/-3

Use operational danger roles for ErrorBoundary fallback styling (web)

• Replaces Tailwind red styling with role-based operational colors for background, border, and foreground. Adds a visible error icon and applies consistent styling to the retry button.

packages/web/src/components/ErrorBoundary.tsx

QueryStatePanel.tsxApply operational danger palette to error panel and retry button (web) +41/-2

Apply operational danger palette to error panel and retry button (web)

• Moves error visuals from CSS color values to operationalStatusColors.danger, including border/background/foreground and retry button styling. Adds explicit role=alert and visible error icon + title for non-color comprehension.

packages/web/src/components/QueryStatePanel.tsx

StatusDot.tsxReplace color-only dot with labeled, accessible status pill (web) +26/-14

Replace color-only dot with labeled, accessible status pill (web)

• Reworks the component from a colored dot to an output pill containing a symbol and text label. Uses operationalStatusColors tones and a role=status aria-label for non-color and assistive-technology clarity.

packages/web/src/components/StatusDot.tsx

SyncProviderCard.tsxMap provider sync UI to operational roles and add visible outcome symbols (web) +43/-15

Map provider sync UI to operational roles and add visible outcome symbols (web)

• Replaces Tailwind status colors with operationalStatusColors for connect/auth/reauth indicators and labels. Changes recent sync history dots into labeled outputs with symbols and accessible aria-labels to avoid color-only meaning.

packages/web/src/components/SyncProviderCard.tsx

index.cssRemove hardcoded query error colors and keep layout-only styling +3/-3

Remove hardcoded query error colors and keep layout-only styling

• Strips the previous translucent red background/text colors from .query-error-panel. Retains structural styles (flex, spacing, border) so colors come from operationalStatusColors-driven inline styles.

packages/web/src/index.css

Refactor (1) +6 / -10
DailyOverview.tsxSimplify ring query error wrapper around QueryStatePanel +6/-10

Simplify ring query error wrapper around QueryStatePanel

• Removes the unused aria-label that included a per-ring label and simplifies the helper signature. Keeps the QueryStatePanel responsible for error semantics.

packages/web/src/components/DailyOverview.tsx

Tests (12) +448 / -6
QueryStatePanel.stories.tsxAdd retryable error Storybook variant for QueryStatePanel (mobile) +9/-0

Add retryable error Storybook variant for QueryStatePanel (mobile)

• Adds a Storybook story demonstrating the retryable error state. Helps visually validate the new retry affordance and messaging.

packages/mobile/components/QueryStatePanel.stories.tsx

QueryStatePanel.test.tsxAssert accessible error styling, contrast, and retry behavior (mobile) +75/-5

Assert accessible error styling, contrast, and retry behavior (mobile)

• Adds contrast-ratio helpers and asserts the error panel uses operational danger surface/border/foreground. Verifies alert role, visible error symbol, retry button behavior, and disabled retry semantics.

packages/mobile/components/QueryStatePanel.test.tsx

RecomputeStatusIndicator.stories.tsxAdd Storybook variants for recompute statuses (mobile) +46/-0

Add Storybook variants for recompute statuses (mobile)

• Introduces Storybook scenarios for active, waiting/indeterminate, delayed, failed, and ready states. Enables quick visual verification of shared indicator mappings.

packages/mobile/components/RecomputeStatusIndicator.stories.tsx

RecomputeStatusIndicator.test.tsxVerify recompute ring stroke uses operational indicator colors (mobile) +4/-0

Verify recompute ring stroke uses operational indicator colors (mobile)

• Adds assertions that the progress ring stroke matches operationalStatusColors.info.indicator. Locks in the new shared-role mapping for recompute indicators.

packages/mobile/components/RecomputeStatusIndicator.test.tsx

SourceProcessingStatusCard.stories.tsxAdd Storybook coverage for source processing outcomes (mobile) +55/-0

Add Storybook coverage for source processing outcomes (mobile)

• Adds stories for waiting, delayed, failed, and complete states. Supports visual validation of indicator mapping and messaging.

packages/mobile/components/SourceProcessingStatusCard.stories.tsx

SourceProcessingStatusCard.test.tsxAssert border indicator mapping to operational roles (mobile) +34/-0

Assert border indicator mapping to operational roles (mobile)

• Adds assertions that the status card’s left border uses danger/success indicators for failed/ready. Verifies mapping is stable across re-renders.

packages/mobile/components/SourceProcessingStatusCard.test.tsx

ErrorBoundary.test.tsxAdd ErrorBoundary fallback a11y and styling test (web) +39/-0

Add ErrorBoundary fallback a11y and styling test (web)

• Adds a jsdom test asserting the fallback is role=alert, shows a visible error symbol, uses operational danger colors, and provides an accessible retry action that triggers onReset.

packages/web/src/components/ErrorBoundary.test.tsx

QueryStatePanel.test.tsxAssert QueryStatePanel error contrast, semantics, and icon (web) +42/-0

Assert QueryStatePanel error contrast, semantics, and icon (web)

• Adds luminance/contrast helpers and verifies error rendering uses operational danger colors with AA text contrast and 3:1 border contrast. Asserts role=alert and a visible error symbol.

packages/web/src/components/QueryStatePanel.test.tsx

RecomputeStatusIndicator.test.tsxVerify recompute ring color is driven by operational roles (web) +3/-0

Verify recompute ring color is driven by operational roles (web)

• Adds assertions that spinner/progress indicators use operationalStatusColors indicator colors for active and delayed states. Locks in consistent cross-platform mapping.

packages/web/src/components/RecomputeStatusIndicator.test.tsx

SourceProcessingStatusCard.test.tsxAssert processing card border indicator mapping (web) +33/-0

Assert processing card border indicator mapping (web)

• Adds tests verifying the card’s left border color matches operational danger/success indicators for failed/ready statuses. Ensures the mapping works via inline styles rather than Tailwind classes.

packages/web/src/components/SourceProcessingStatusCard.test.tsx

StatusDot.test.tsxAdd tests for StatusDot non-color cues and contrast (web) +62/-0

Add tests for StatusDot non-color cues and contrast (web)

• Introduces tests ensuring each sync status renders a visible label and symbol, plus role=status naming. Asserts WCAG-level contrast for foreground and non-text borders on both tone surfaces and a white card surface.

packages/web/src/components/StatusDot.test.tsx

SyncProviderCard.test.tsxAdd assertions for operational color mapping and non-color sync outcomes (web) +46/-1

Add assertions for operational color mapping and non-color sync outcomes (web)

• Validates Connect/Reconnect text colors map to operational info/warning foregrounds. Adds expectations for visible ✓/! outcome symbols and operational indicator usage for push-only status dots.

packages/web/src/components/SyncProviderCard.test.tsx

Documentation (2) +83 / -0
2026-07-27-accessible-operational-colors.mdAdd TDD plan for accessible operational colors rollout +76/-0

Add TDD plan for accessible operational colors rollout

• Introduces a structured plan covering scope, evidence, WCAG targets, and tasks. Documents the intended mappings and the test strategy focusing on rendered contrast and non-color cues.

docs/superpowers/plans/2026-07-27-accessible-operational-colors.md

README.mdDocument operationalStatusColors purpose and WCAG targets +7/-0

Document operationalStatusColors purpose and WCAG targets

• Adds documentation explaining that operationalStatusColors is presentation-only for generic application states. Links WCAG contrast guidance and clarifies it does not redefine domain meaning.

packages/scoring/README.md

@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Mobile Preview

Scan to open on device:

QR code for dofek://preview/pr-2226

Channel pr-2226
Deep Link dofek://preview/pr-2226
Commit 1278d57

To test on device:

  1. Build and install the preview client: PREVIEW_CHANNEL=pr-2226 pnpm expo prebuild --clean -p ios
  2. Or tap deep link on an existing preview build: dofek://preview/pr-2226

Each PR gets its own channel. Build a preview client with PREVIEW_CHANNEL=pr-{N} to test.

@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Storybook previews for 1278d571 are ready:

This comment updates automatically on each PR push.

@qodo-code-review

qodo-code-review Bot commented Jul 27, 2026

Copy link
Copy Markdown

Code Review by Qodo

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

Context used
✅ Compliance rules (platform): 186 rules

Grey Divider


Action required

1. Ring error context lost ✓ Resolved 🐞 Bug ≡ Correctness
Description
DailyOverview renders multiple ring error panels via RingQueryError, but the wrapper no longer
provides any ring-specific accessible label/context; QueryStatePanel displays a generic error
heading for all errors. When multiple ring queries fail, assistive tech will announce multiple
indistinguishable alerts, making it unclear whether Recovery/Strain/Sleep failed.
Code

packages/web/src/components/DailyOverview.tsx[R509-513]

+function RingQueryError({ error }: { error: unknown }) {
  return (
-    <div role="alert" aria-label={`${label} error`} className="max-w-[180px] text-center text-xs">
+    <div className="max-w-[180px] text-center text-xs">
      <QueryStatePanel error={error} height={72} />
    </div>
Relevance

⭐⭐⭐ High

Similar pattern previously accepted: RingQueryError kept ring-specific role/aria-label context (PR
#1968). This change removes it.

PR-#1968

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
DailyOverview renders multiple RingQueryError instances (for different ring queries), but
RingQueryError does not provide a label and QueryStatePanel always renders the same generic
error heading, so alerts lack ring-specific context.

packages/web/src/components/DailyOverview.tsx[509-515]
packages/web/src/components/DailyOverview.tsx[611-696]
packages/web/src/components/QueryStatePanel.tsx[23-83]

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

### Issue description
`DailyOverview` uses a shared `RingQueryError` wrapper to render multiple ring-specific failures, but the wrapper no longer supplies a ring-specific accessible label, and `QueryStatePanel`’s error UI is generic. Multiple failures can’t be distinguished in screen-reader alert announcements.

### Issue Context
`DailyOverview` can render up to four ring error panels (recovery, strain, strain target, sleep). `QueryStatePanel` now uses `role="alert"` with a generic heading (“Could not load this section”), so without additional context each alert sounds the same.

### Fix Focus Areas
- packages/web/src/components/DailyOverview.tsx[509-515]
- packages/web/src/components/DailyOverview.tsx[611-696]
- packages/web/src/components/QueryStatePanel.tsx[23-82]

### Suggested fix
- Re-introduce a label/context for each ring error, e.g.:
 - change `RingQueryError` back to accept a `label` ("Recovery", "Strain", "Sleep"),
 - extend `QueryStatePanel` with an optional `ariaLabel`/`contextLabel` prop,
 - set `aria-label={\`Recovery error\`}` on the alert container and/or render a contextual heading like `Recovery: Could not load this section`.
- Update `DailyOverview` call sites to pass the appropriate label for each ring’s error panel.

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



Remediation recommended

2. Hardcoded sizes in QueryStatePanel ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
packages/mobile/components/QueryStatePanel.tsx introduces hardcoded visual constants (e.g.,
fontSize: 14, height: 24, width: 24, lineHeight: 22, fontWeight: "800") instead of using
shared design tokens. This violates the design-token requirement and makes visual consistency and
theming harder to maintain.
Code

packages/mobile/components/QueryStatePanel.tsx[R131-159]

+  errorIcon: {
+    borderColor: operationalStatusColors.danger.border,
+    borderRadius: radius.full,
+    borderWidth: 1,
+    color: operationalStatusColors.danger.foreground,
+    fontSize: 14,
+    fontWeight: "800",
+    height: 24,
+    lineHeight: 22,
+    textAlign: "center",
+    width: 24,
  },
  emptyMessage: {
    color: colors.textSecondary,
  },
+  retryButton: {
+    backgroundColor: operationalStatusColors.danger.surface,
+    borderColor: operationalStatusColors.danger.border,
+    borderRadius: radius.md,
+    borderWidth: 1,
+    marginTop: spacing.sm,
+    paddingHorizontal: spacing.md,
+    paddingVertical: spacing.xs,
+  },
+  retryText: {
+    color: operationalStatusColors.danger.foreground,
+    fontSize: 12,
+    fontWeight: "700",
+  },
Relevance

⭐⭐⭐ High

Team pushes replacing new hardcoded RN style literals with shared tokens (partially accepted in PR
#2216; padding literal flagged PR #824).

PR-#2216
PR-#824

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 721961 requires using shared tokens instead of introducing new hardcoded visual
literals in UI code. The new styles in QueryStatePanel add multiple hardcoded numeric sizes and
font weights rather than referencing fontSize/fontWeight/spacing tokens.

Rule 721961: Use design tokens instead of hardcoded visual constants
packages/mobile/components/QueryStatePanel.tsx[110-114]
packages/mobile/components/QueryStatePanel.tsx[131-159]

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

## Issue description
`QueryStatePanel` introduces hardcoded visual constants in React Native styles (font sizes/weights and fixed dimensions). The compliance rule requires using shared design tokens (from `@dofek/scoring/tokens` via `packages/mobile/theme.ts`) instead of new literals.

## Issue Context
The repo already exports `fontSize`, `fontWeight`, `spacing`, and `radius` from `packages/mobile/theme.ts`. New style additions like the error icon sizing and retry text should reference those tokens (and consider `StyleSheet.hairlineWidth` for 1px borders) to keep UI consistent.

## Fix Focus Areas
- packages/mobile/components/QueryStatePanel.tsx[110-114]
- packages/mobile/components/QueryStatePanel.tsx[131-159]

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


3. Retry border/hover broken ✓ Resolved 🐞 Bug ≡ Correctness
Description
In ErrorBoundary, the “Try again” button sets borderWidth/borderColor but never sets a border
style (or Tailwind border), so the border may not render; additionally, the inline
backgroundColor overrides the Tailwind hover background class so the button has no hover feedback.
This reduces visible affordance/contrast cues in the primary recovery action for fatal render
errors.
Code

packages/web/src/components/ErrorBoundary.tsx[R71-77]

            className="px-3 py-1.5 text-xs font-medium rounded-md bg-accent/10 text-foreground hover:bg-surface-hover transition-colors"
+            style={{
+              backgroundColor: errorTone.surface,
+              borderColor: errorTone.border,
+              color: errorTone.foreground,
+              borderWidth: 1,
+            }}
Relevance

⭐⭐ Medium

No close historical finding on Tailwind hover being overridden / missing border-style; correctness
issue but evidence unclear.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The button’s inline style sets borderWidth/borderColor but no borderStyle and no Tailwind
border utility is present; inline backgroundColor also overrides the hover background utility,
preventing hover feedback.

packages/web/src/components/ErrorBoundary.tsx[41-81]

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 ErrorBoundary retry button is styled with inline `backgroundColor` and border properties, but it lacks a border style/class and its hover class is ineffective due to inline style precedence.

### Issue Context
This PR introduces operational-tone inline styles in the ErrorBoundary. Inline `backgroundColor` wins over Tailwind’s `hover:bg-*` rules, and `borderWidth` without `borderStyle`/`border` means the border is not reliably rendered.

### Fix Focus Areas
- packages/web/src/components/ErrorBoundary.tsx[65-80]

### Suggested fix
- Ensure the border actually renders by either:
 - adding a Tailwind `border` class to the button (recommended), or
 - setting `style={{ borderStyle: "solid" }}` (and/or using `border: "1px solid"`).
- Restore visible interaction feedback without relying on `hover:bg-*` while using inline background, e.g. replace `hover:bg-surface-hover` with a property that isn’t overridden (like `hover:brightness-95`), or move tone styling into CSS variables/classes so hover can still change background.

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



Informational

4. Inline styles in QueryStatePanel 📘 Rule violation ⚙ Maintainability
Description
packages/web/src/components/QueryStatePanel.tsx adds inline style={...} blocks to apply layout
and color styling instead of Tailwind utility classes. This violates the requirement to use Tailwind
utilities for styling and avoid custom CSS/inline styles except for truly non-expressible dynamic
cases.
Code

packages/web/src/components/QueryStatePanel.tsx[R50-97]

    <div
      className={variant === "error" ? "query-error-panel" : "query-state-panel"}
-      style={{ minHeight: height }}
+      style={{
+        minHeight: height,
+        ...(variant === "error"
+          ? {
+              backgroundColor: errorTone.surface,
+              borderColor: errorTone.border,
+              color: errorTone.foreground,
+            }
+          : {}),
+      }}
      data-testid={`query-state-${variant}`}
+      role={variant === "error" ? "alert" : undefined}
    >
-      <p>{resolvedMessage}</p>
+      {variant === "error" ? (
+        <>
+          <span
+            aria-hidden="true"
+            className="flex h-6 w-6 items-center justify-center rounded-full border text-sm font-bold"
+            data-testid="query-state-error-icon"
+            style={{ borderColor: errorTone.border, color: errorTone.foreground }}
+          >
+            !
+          </span>
+          <h2 className="text-sm font-semibold" style={{ color: errorTone.foreground }}>
+            Could not load this section
+          </h2>
+        </>
+      ) : null}
+      <p style={variant === "error" ? { color: errorTone.foreground } : undefined}>
+        {resolvedMessage}
+      </p>
      {onRetry ? (
        <button
          type="button"
          disabled={retrying}
          onClick={onRetry}
          className="mt-3 text-xs px-3 py-1.5 rounded bg-accent/10 border border-border-strong text-foreground disabled:text-dim disabled:cursor-not-allowed"
+          style={
+            variant === "error"
+              ? {
+                  backgroundColor: errorTone.surface,
+                  borderColor: errorTone.border,
+                  color: errorTone.foreground,
+                }
+              : undefined
+          }
Relevance

⭐ Low

Repo commonly accepts inline styles for dynamic token-driven colors (e.g., operationalStatusColors
usage in PRs #1359, #2222).

PR-#1359
PR-#2222

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 722046 requires Tailwind utility classes for styling and flags new/modified inline
styles for standard visual/layout rules. The changed QueryStatePanel adds inline style objects
for background/border/text colors (and related styling) rather than using Tailwind utilities.

Rule 722046: Use Tailwind CSS utility classes for all styling
packages/web/src/components/QueryStatePanel.tsx[50-97]

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 updated web `QueryStatePanel` uses inline style objects to set `backgroundColor`, `borderColor`, and `color` (and additional inline styles for the icon and retry button). The Tailwind compliance rule requires styling via Tailwind utilities and discourages inline styles/custom CSS for standard layout/visual styling.

## Issue Context
The inline styles are currently used to apply operational-status tones. To comply, consider:
- expressing these styles via Tailwind classes backed by CSS variables (or Tailwind theme extension), or
- using utility-like classes that map to the operational roles, rather than per-element inline styles.

## Fix Focus Areas
- packages/web/src/components/QueryStatePanel.tsx[50-97]

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


Grey Divider

Qodo Logo

Comment thread packages/mobile/components/QueryStatePanel.tsx
Comment thread packages/web/src/components/ErrorBoundary.tsx Outdated
Comment thread packages/web/src/components/DailyOverview.tsx Outdated
@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

@Asherlc

Asherlc commented Jul 27, 2026

Copy link
Copy Markdown
Owner Author

Review follow-up for Qodo optional finding “Inline styles in QueryStatePanel”: skipped with rationale.

The inline declarations apply runtime values from the canonical cross-platform @dofek/scoring operational color tokens. Replacing them with web-only Tailwind literals or duplicated CSS variables would create a second source of truth and break the shared web/mobile token contract that this issue establishes. Structural/layout styling remains in Tailwind classes; the existing dynamic minHeight prop and shared token values are the only inline values. This matches existing repository practice for dynamic token-driven colors, so no code change is warranted.

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

@Asherlc
Asherlc merged commit 981e399 into main Jul 28, 2026
100 checks passed
@Asherlc
Asherlc deleted the codex/issue-2068 branch July 28, 2026 00:21
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.

[P0-10] Critical text contrast failures including ~1.12:1 error text

2 participants