Skip to content

feat(ui): accessibility audit and UX optimization#34

Open
4Clover wants to merge 12 commits intodevfrom
feat/ui-audit-optimization
Open

feat(ui): accessibility audit and UX optimization#34
4Clover wants to merge 12 commits intodevfrom
feat/ui-audit-optimization

Conversation

@4Clover
Copy link
Copy Markdown
Owner

@4Clover 4Clover commented Mar 6, 2026

Why

Pre-alpha UI audit surfaced accessibility gaps and mobile UX issues — hardcoded colors failing contrast checks, missing keyboard/screen-reader support, no touch gestures for core interactions, and several WCAG violations. These need to be resolved before any alpha user testing.

What Changed

Wave 1 — Accessibility foundation (fix(ui))

  • Consolidated 52 hardcoded color tokens to theme variables for consistent contrast
  • Added skip-to-content link, iOS safe-area insets, and keyboard navigation
  • Fixed hover-only affordances (chip remove buttons now always visible on touch)
  • Added aria-live regions, form autocomplete attributes, and selective text-xs sizing improvements

Wave 2 — Structural improvements (feat(ui))

  • Replaced opacity-based contrast patterns with contrast-safe design tokens
  • Added raw touch swipe gestures to SwipeablePlayerRow (draft action on swipe)
  • Wired prefers-reduced-motion into swipe animations

Wave 3 — Screen reader polish (fix(a11y))

  • Replaced generic "Search results updated" with "Showing N of M players" in rankings aria-live region (WCAG SC 4.1.3)

All 742 tests pass across all three waves, zero regressions.

Learnings

  • Opacity ≠ contrast: Using opacity-60 on text creates contrast ratios that depend on the background — unreliable for WCAG AA. Switched to explicit text-muted-foreground / text-muted tokens that guarantee 4.5:1 regardless of context.
  • Touch events on virtual lists: React's synthetic touch events can conflict with virtualized scroll containers. Used raw touchstart/touchmove/touchend with useRef tracking to avoid interfering with scroll momentum.
  • aria-live specificity matters: Generic status messages ("results updated") are noise for screen readers. Concrete messages ("Showing 12 of 48 players") provide actionable context without additional navigation.
  • Safe-area insets: env(safe-area-inset-bottom) needs to be composed with existing padding via calc() — can't just swap in the env value or you lose the original spacing.

  • CI passes (pnpm validate)

4Clover added 3 commits March 5, 2026 06:52
- Consolidate 52 hardcoded color tokens to theme variables
- Selective text-xs sizing improvements for mobile readability
- Add skip-to-content link in dashboard layout
- Add iOS safe area insets for mobile bottom nav
- Fix hover-only affordances (chip remove buttons)
- Add keyboard navigation for interactive elements
- Add form autocomplete attributes
- Add aria-live regions for dynamic content

All 742 tests pass, zero regressions
- Fix CSS contrast issues (opacity patterns → contrast-safe tokens)
- Add touch swipe gestures to SwipeablePlayerRow (raw touch events)
- Respect prefers-reduced-motion in swipe animations

All 742 tests pass, zero regressions
Replaces generic 'Search results updated' with 'Showing N of M players'
to satisfy WCAG SC 4.1.3 (Status Messages) — screen readers now announce
the actual filtered count when search/filter state changes.
Copilot AI review requested due to automatic review settings March 6, 2026 08:47
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses pre-alpha UI accessibility gaps and mobile UX issues by replacing hardcoded/opacity-based styling with theme tokens, improving readable sizing/contrast, and adding keyboard/screen-reader enhancements including swipe-to-draft on mobile rankings.

Changes:

  • Standardized UI text sizing and replaced hardcoded grays/whites with theme tokens (e.g., text-muted-foreground) across trade/roster/draft/dashboard UI.
  • Improved accessibility semantics: skip-to-content link, safe-area viewport support, better form labeling and ARIA attributes, and loading aria-busy regions.
  • Added mobile swipe gesture support to draft rankings rows and wired prefers-reduced-motion into the swipe animation.

Reviewed changes

Copilot reviewed 46 out of 46 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/components/waiver/faab-bid-slider.tsx Increased helper text size for readability.
src/components/transactions/transaction-row.tsx Increased timestamp text size for readability.
src/components/transactions/transaction-details.tsx Increased secondary detail text size for readability.
src/components/trade/trade-partner-finder.tsx Replaced hardcoded gray with theme token; increased description/detail text sizes.
src/components/trade/trade-explanation.tsx Replaced hardcoded gray with theme token; increased explanatory text size.
src/components/trade/trade-builder.tsx Replaced hardcoded gray with theme token; increased helper/empty-state text sizes.
src/components/trade/player-picker.tsx Replaced hardcoded gray with theme token across controls and results.
src/components/trade/player-chip.tsx Made drag/remove affordances visible on touch/focus; replaced hardcoded gray with tokens.
src/components/trade/pick-chip.tsx Same as player chips: touch/focus visibility + tokenized colors.
src/components/trade/format-toggle.tsx Replaced hardcoded gray with theme token for inactive state.
src/components/trade/fairness-meter.tsx Replaced hardcoded gray with theme token in labels.
src/components/trade/bias-slider.tsx Replaced hardcoded gray with theme token; standardized label colors.
src/components/roster/lineup-slot.tsx Improved empty-slot styling and contrast; increased metadata/injury text sizes.
src/components/roster/lineup-explanation.tsx Increased explanatory text sizes for readability.
src/components/roster/lineup-comparison.tsx Replaced hardcoded colors with tokens; improved contrast/readability in summary cards.
src/components/roster/apply-optimization-button.tsx Increased secondary text sizes in dialog content.
src/components/layout/user-menu.tsx Increased email/subtext size for readability.
src/components/layout/mobile-nav.tsx Added safe-area bottom padding to loading fallback nav.
src/components/layout/mobile-nav-animated.tsx Added safe-area bottom padding; slightly increased label text size.
src/components/gamification/streak-indicator.tsx Increased warning text size for readability.
src/components/gamification/achievement-badge.tsx Increased unlocked date text size for readability.
src/components/feedback/feedback-form.tsx Added explicit label associations/ids and improved ARIA for star rating.
src/components/draft/swipeable-player-row.tsx Implemented swipe-to-draft gesture + keyboard support + reduced-motion transitions.
src/components/draft/simulation-overlay.tsx Increased simulation status text size.
src/components/draft/rankings-list.tsx Updated drafted-row styling; added SR-only assertive aria-live for celebrations; passed new props to swipe row.
src/components/draft/rankings-controls.tsx Added SR-only aria-live announcing “Showing N of M players”.
src/components/draft/pick-explanation.tsx Increased algorithm info text size.
src/components/draft/my-team-sidebar.tsx Increased sidebar metadata text size.
src/components/draft/keeper-section.tsx Increased keeper metadata text size.
src/components/draft/explanation-panel.tsx Increased explanatory paragraph text size.
src/components/draft/draft-view-tabs.tsx Adjusted responsive grid to include an md breakpoint for improved layout.
src/components/draft/draft-rankings.tsx Passed result/total counts into RankingsControls for better aria-live messaging.
src/app/layout.tsx Added viewport config for iOS safe-area support (viewport-fit=cover).
src/app/(sandbox)/draft-sandbox/sandbox-draft-view.tsx Updated responsive grid to include md breakpoint.
src/app/(sandbox)/draft-sandbox/league-form.tsx Increased helper text sizes for readability.
src/app/(dashboard)/waivers/waivers-client.tsx Updated responsive grid to include md breakpoint.
src/app/(dashboard)/waivers/loading.tsx Added aria-live/aria-busy to loading container; updated responsive grid.
src/app/(dashboard)/trade/trade-client.tsx Replaced hardcoded gray with theme token; increased methodology/caveats text sizes.
src/app/(dashboard)/trade/loading.tsx Added aria-live/aria-busy to loading container.
src/app/(dashboard)/roster/loading.tsx Added aria-live/aria-busy to loading container.
src/app/(dashboard)/layout.tsx Added skip-to-content link and main anchor target id.
src/app/(dashboard)/draft/loading.tsx Added aria-live/aria-busy to loading container; updated responsive grid.
src/app/(dashboard)/dashboard/page.tsx Increased secondary description text size.
src/app/(dashboard)/dashboard/league-card.tsx Increased stat subtext size.
src/app/(dashboard)/connect/page.tsx Improved autocomplete, added keyboard operability/ARIA for selectable league cards.
src/app/(auth)/login/page.tsx Added email autocomplete to login form field.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +83 to +92
{isDraftable && (
<button
type="button"
onClick={() => onDraft(playerId)}
className="absolute left-0 top-0 h-full w-24 bg-green-600 text-sm font-semibold text-white"
aria-label={`Draft ${playerName}`}
>
Draft
</button>
)}
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

The left-side "Draft" is always rendered (when isDraftable), even when it’s fully covered by the row content at offsetX=0. That keeps it in the tab order and focusable while not visually discoverable, which is an a11y issue. Consider only rendering/enabling it once the swipe reveal is active, or set tabIndex={-1}/aria-hidden/disabled (and possibly pointer-events-none) while offsetX is below the reveal threshold.

Copilot uses AI. Check for mistakes.
Comment on lines +93 to +103
<div
role="button"
tabIndex={0}
aria-label={`${playerName}, ${position}, VBD ${vbd.toFixed(1)}${isDraftable ? ', press Enter to draft' : ''}`}
onClick={handleClick}
onKeyDown={handleKeyDown}
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
onTouchCancel={handleTouchEnd}
style={{
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

role="button"/tabIndex={0} and keyboard handlers are applied even when isDraftable is false, which makes non-actionable rows appear interactive to keyboard and screen-reader users. Consider conditionally setting tabIndex/role (or aria-disabled) and only attaching click/key handlers when drafting is actually available.

Copilot uses AI. Check for mistakes.
4Clover added 6 commits March 6, 2026 04:39
- Add dev-only magic link login bypass with Sleeper auto-connect
- Add ADP proxy route to bypass browser CORS in sandbox
- Use upsert for algorithm cache writes (avoids delete+insert race)
- Use no-cache for getAllPlayers (18MB exceeds Next.js 2MB limit)
- Extract viewport to exported const (Next.js best practice)
- Polish simulation overlay and risk slider responsive layout
- Update CSP for PostHog assets, FFC, and web workers
- Pre-fill sandbox league ID from NEXT_PUBLIC_SANDBOX_LEAGUE_ID
Improve mobile touch targets to 44px minimum across roster week
selector, waivers inputs, trade builder buttons, feedback stars,
bias slider thumb, and format toggle labels. Bump muted-foreground
contrast from 0.70 to 0.75 and swap green/red-400 to 500 for WCAG
compliance. Fix roster optimizer empty slot rendering when starter
count exceeds player count. Add horizontal scroll for draft position
filters and week selector on mobile. Add E2E tests for zero-improvement
badge suppression and empty slot display.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 6, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 05be293f-29f8-40cc-b129-0f3cf0c8f39b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/ui-audit-optimization

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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 and usage tips.

4Clover added 3 commits March 6, 2026 12:57
Simplify CLI architecture with dedicated modules for args parsing,
screenshot capture, and dev server. Untrack generated-prompts directory
as output artifacts should not be version controlled.
next start sets NODE_ENV=production, which caused the dev-login
endpoint to return 404. The capture script then silently continued
without auth, so protected routes rendered login pages instead of
actual content.

Pass ENABLE_DEV_LOGIN=true from the serve script and check for it
in the dev-login gate alongside NODE_ENV.
Capture script was screenshotting pages before data finished loading,
resulting in skeleton placeholders being flagged as visual bugs. Now
waits for networkidle + skeleton element removal before capture.
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.

2 participants