Skip to content

fix(a11y): zero jsx-a11y warnings in non-test files + lint ratchet CI gate - #263

Closed
njrini99-code wants to merge 1 commit into
mainfrom
fix/a11y-lint-batch
Closed

fix(a11y): zero jsx-a11y warnings in non-test files + lint ratchet CI gate#263
njrini99-code wants to merge 1 commit into
mainfrom
fix/a11y-lint-batch

Conversation

@njrini99-code

@njrini99-code njrini99-code commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fixes 575+ jsx-a11y warnings across src/app and src/components (golf, fairway, coach, baseball, admin CRM, help) — 0 jsx-a11y warnings remain outside 2 excluded test files.
  • Fix patterns: useId+htmlFor/id wiring for form labels; clickable divs/rows and modal backdrops converted to <button type="button">; redundant role="list" removed from <ul>; justified inline disables only for drag-and-drop zones, stopPropagation-only wrappers, hover-tooltip triggers, intentional dialog autofocus, and custom component role props (not ARIA roles).
  • Lint ratchet: scripts/lint-ratchet.mjs + .lint-baseline.json lock per-rule warning counts (2,394 warnings / 11 rules, down from 2,846 / 22). npm run lint:ratchet fails if any rule's count rises; --update re-locks after intentional burn-down. Wired into ci.yml after the lint step.
  • No behavior or visual changes intended — markup-level semantics only.

Test plan

  • npx tsc --noEmit clean
  • Full unit suite green (background run, exit 0); component suites re-run after final edits (33 passed)
  • npx eslint src → jsx-a11y only in the 2 excluded test files
  • Ratchet validated including a simulated regression (fails as expected)

🤖 Generated with Claude Code


Open in Devin Review

…atchet CI gate

- 575+ jsx-a11y warnings fixed across src/app and src/components
  (label/control wiring via useId, clickable divs -> buttons, modal
  backdrops -> buttons, redundant role=list removed, justified disables
  for drag-drop/stopPropagation/tooltip/autofocus cases)
- 0 jsx-a11y warnings remain outside 2 excluded test files
- scripts/lint-ratchet.mjs: per-rule warning baseline (.lint-baseline.json,
  2394 warnings/11 rules); 'npm run lint:ratchet' fails CI if any rule
  count rises; --update re-locks after intentional burn-down
- ci.yml: ratchet step added after lint

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Jun 10, 2026

Copy link
Copy Markdown

Too many files changed for review. (174 files found, 100 file limit)

@vercel

vercel Bot commented Jun 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
helmv3 Ready Ready Preview, Comment Jun 10, 2026 4:35pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 174 files, which is 24 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b3b2643d-111e-467d-89ac-aefb4370947f

📥 Commits

Reviewing files that changed from the base of the PR and between 0aac78b and 21c7bcb.

📒 Files selected for processing (174)
  • .github/workflows/ci.yml
  • .lint-baseline.json
  • package.json
  • scripts/lint-ratchet.mjs
  • src/app/baseball/(auth)/complete-signup/CompleteSignupClient.tsx
  • src/app/baseball/(auth)/forgot-password/page.tsx
  • src/app/baseball/(auth)/reset-password/page.tsx
  • src/app/baseball/(coach-dashboard)/coach/layout.tsx
  • src/app/baseball/(dashboard)/dashboard/discover/page.tsx
  • src/app/baseball/(dashboard)/dashboard/events/page.tsx
  • src/app/baseball/(dashboard)/dashboard/pipeline/page.tsx
  • src/app/baseball/(dashboard)/dashboard/program/page.tsx
  • src/app/baseball/(dashboard)/dashboard/roster/page.tsx
  • src/app/baseball/(dashboard)/dashboard/settings/page.tsx
  • src/app/baseball/(dashboard)/dashboard/stats/games/new/NewGameClient.tsx
  • src/app/baseball/(dashboard)/dashboard/team/TeamDashboardClient.tsx
  • src/app/baseball/(dashboard)/dashboard/teams/page.tsx
  • src/app/baseball/(dashboard)/dashboard/videos/page.tsx
  • src/app/baseball/(dashboard)/dashboard/watchlist/WatchlistClient.tsx
  • src/app/baseball/(onboarding)/coach-onboarding/page.tsx
  • src/app/baseball/(onboarding)/player/page.tsx
  • src/app/baseball/(player-dashboard)/player/layout.tsx
  • src/app/global-error.tsx
  • src/app/golf/(auth)/forgot-password/page.tsx
  • src/app/golf/(auth)/reset-password/page.tsx
  • src/app/golf/(auth)/signup/page.tsx
  • src/app/golf/(dashboard)/dashboard/components/CoachDashboard.tsx
  • src/app/golf/(dashboard)/dashboard/components/PlayerDashboard.tsx
  • src/app/golf/(dashboard)/dashboard/development/development-client.tsx
  • src/app/golf/(dashboard)/dashboard/documents/documents-client.tsx
  • src/app/golf/(dashboard)/dashboard/messages/page.tsx
  • src/app/golf/(dashboard)/dashboard/my-development/LogProgressButton.tsx
  • src/app/golf/(dashboard)/dashboard/rounds/new/new-round-client.tsx
  • src/app/golf/(dashboard)/dashboard/settings/coaching-intelligence/page.tsx
  • src/app/golf/(dashboard)/dashboard/settings/page.tsx
  • src/app/golf/(dashboard)/dashboard/team/page.tsx
  • src/app/golf/(dashboard)/dashboard/team/team-settings-client.tsx
  • src/app/golf/(dashboard)/dashboard/travel/travel-client.tsx
  • src/app/golf/(onboarding)/coach/page.tsx
  • src/app/golf/(onboarding)/player/page.tsx
  • src/app/golf/admin/components/HealthScoreBreakdownModal.tsx
  • src/app/golf/admin/components/UserDetailPanel.tsx
  • src/app/golf/admin/components/tracer/DataQualityIssueRow.tsx
  • src/app/golf/admin/crm/components/AddCoachModal.tsx
  • src/app/golf/admin/crm/components/BulkEmailModal.tsx
  • src/app/golf/admin/crm/components/CalendarView.tsx
  • src/app/golf/admin/crm/components/CoachDetailPanel.tsx
  • src/app/golf/admin/crm/components/CoachTable.tsx
  • src/app/golf/admin/crm/components/ContactLogModal.tsx
  • src/app/golf/admin/crm/components/EventDetailModal.tsx
  • src/app/golf/admin/crm/components/ImportModal.tsx
  • src/app/golf/admin/crm/components/PipelineView.tsx
  • src/app/golf/admin/crm/components/QuickActionsPanel.tsx
  • src/app/golf/admin/crm/components/QuickActionsToolbar.tsx
  • src/app/golf/admin/crm/components/ScheduleEventModal.tsx
  • src/app/golf/admin/crm/components/automations/AutomationEditor.tsx
  • src/app/golf/admin/crm/components/notes/AddNoteDialog.tsx
  • src/app/golf/admin/crm/components/resend/ResendActivityView.tsx
  • src/app/golf/admin/crm/components/segments/SaveSegmentDialog.tsx
  • src/app/golf/admin/crm/components/segments/SavedSegmentsRail.tsx
  • src/app/golf/admin/crm/components/sequences/EnrollSegmentDialog.tsx
  • src/app/golf/admin/crm/components/sequences/SequenceCard.tsx
  • src/app/golf/admin/crm/components/sequences/SequencesList.tsx
  • src/app/golf/admin/crm/components/tasks/CreateTaskDialog.tsx
  • src/app/golf/admin/crm/components/tasks/TaskCard.tsx
  • src/app/golf/admin/crm/components/tasks/TasksDueWidget.tsx
  • src/app/golf/admin/page.tsx
  • src/app/golf/join/page.tsx
  • src/app/help/page.tsx
  • src/components/baseball/announcements/CreateAnnouncementFlow.tsx
  • src/components/baseball/announcements/PlayerSelector.tsx
  • src/components/baseball/announcements/UrgencyPicker.tsx
  • src/components/baseball/command-center/BaseballInviteButton.tsx
  • src/components/baseball/documents/DocumentCard.tsx
  • src/components/baseball/documents/DocumentPreview.tsx
  • src/components/baseball/documents/UploadNewVersionModal.tsx
  • src/components/baseball/player-profile/PlayerProfileClient.tsx
  • src/components/baseball/position-planner/PositionPlanner.tsx
  • src/components/baseball/profile/CollegeProfileEditor.tsx
  • src/components/baseball/recruiting-philosophy/PositionPriorityRanker.tsx
  • src/components/baseball/recruiting-philosophy/RecruitingWeightDistributor.tsx
  • src/components/baseball/roster/PlayerCard.tsx
  • src/components/baseball/roster/RosterToolbar.tsx
  • src/components/baseball/settings/PhilosophySettingsClient.tsx
  • src/components/baseball/stats/StatsUploadClient.tsx
  • src/components/baseball/tasks/CreateTaskModal.tsx
  • src/components/baseball/tasks/ReminderPicker.tsx
  • src/components/baseball/team/BatchVideoUpload.tsx
  • src/components/baseball/travel/CreateItineraryModal.tsx
  • src/components/baseball/travel/ExpenseForm.tsx
  • src/components/baseball/travel/ExpenseList.tsx
  • src/components/baseball/travel/TravelClient.tsx
  • src/components/coach/CreateCampModal.tsx
  • src/components/coach/CreateDevPlanModal.tsx
  • src/components/coach/InviteModal.tsx
  • src/components/coach/PlayerDetailModal.tsx
  • src/components/coach/discover/FilterPanel.tsx
  • src/components/coach/discover/PlayerCard.tsx
  • src/components/coach/discover/PlayerCardGrid.tsx
  • src/components/coach/discover/PlayerHoverPreview.tsx
  • src/components/coach/lineup/LineupBuilder.tsx
  • src/components/fairway/data-table/data-table.tsx
  • src/components/fairway/pages/calendar/FairwayEventEditor.tsx
  • src/components/fairway/pages/coachhelm/FairwayBrief.tsx
  • src/components/fairway/pages/coachhelm/FairwayCoachHelmSignals.tsx
  • src/components/fairway/pages/coachhelm/FairwayEffectiveness.tsx
  • src/components/fairway/pages/coachhelm/FairwayMyDevelopment.tsx
  • src/components/fairway/pages/coachhelm/FairwayPlayerCoachHelm.tsx
  • src/components/fairway/pages/coachhelm/FairwayPlayerInsight.tsx
  • src/components/fairway/pages/coachhelm/FairwayPlayerStats.tsx
  • src/components/fairway/pages/coachhelm/GenomeCompareView.tsx
  • src/components/fairway/pages/coachhelm/GenomeDetailView.tsx
  • src/components/fairway/pages/coachhelm/PlayersGridView.tsx
  • src/components/fairway/pages/messages/MessageThreadPane.tsx
  • src/components/fairway/pages/rounds-new/FairwayRecentCourses.tsx
  • src/components/fairway/pages/tasks/FairwayCreateTaskModal.tsx
  • src/components/features/profile-editor.tsx
  • src/components/features/video-player.tsx
  • src/components/features/video-upload.tsx
  • src/components/golf/CommandPalette.tsx
  • src/components/golf/ShotTrackingComprehensive.tsx
  • src/components/golf/announcements/CreateAnnouncementFlow.tsx
  • src/components/golf/calendar/AvailabilityDayView.tsx
  • src/components/golf/calendar/CalendarAvatarSidebar.tsx
  • src/components/golf/calendar/CalendarSyncButton.tsx
  • src/components/golf/calendar/CreateFeedSection.tsx
  • src/components/golf/calendar/EventDetailModal.tsx
  • src/components/golf/calendar/MobileEventSheet.tsx
  • src/components/golf/calendar/NotificationCenter.tsx
  • src/components/golf/calendar/PremiumEventBlock.tsx
  • src/components/golf/calendar/QuickAddEventFAB.tsx
  • src/components/golf/classes/AddClassModal.tsx
  • src/components/golf/classes/ConfirmClassesModal.tsx
  • src/components/golf/classes/UploadScheduleModal.tsx
  • src/components/golf/coachhelm/PromoteToFocusAreaButton.tsx
  • src/components/golf/coachhelm/analytics/TrendIndicator.tsx
  • src/components/golf/coachhelm/insight-card/InsightCard.tsx
  • src/components/golf/coachhelm/insights/InsightExportModal.tsx
  • src/components/golf/coachhelm/insights/InsightFiltersPanel.tsx
  • src/components/golf/coachhelm/patterns/PatternDashboard.tsx
  • src/components/golf/coachhelm/patterns/PatternValidationModal.tsx
  • src/components/golf/coachhelm/v3/Genome/GenomeComparePicker.tsx
  • src/components/golf/coachhelm/v3/IntentDrawer/index.tsx
  • src/components/golf/coachhelm/v3/QualifyingBoard/CoachPickPanel.tsx
  • src/components/golf/coachhelm/v3/QualifyingBoard/LeaderboardWithSlots.tsx
  • src/components/golf/dashboard/today-timeline.tsx
  • src/components/golf/documents/DocumentPreview.tsx
  • src/components/golf/documents/ImagePreview.tsx
  • src/components/golf/documents/UploadNewVersionModal.tsx
  • src/components/golf/messages/GolfNewMessageModal.tsx
  • src/components/golf/messages/GolfTeamBroadcastModal.tsx
  • src/components/golf/recruiting/RecruitFormSheet.tsx
  • src/components/golf/roster/InvitePlayerButton.tsx
  • src/components/golf/roster/PlayerStatusBadge.tsx
  • src/components/golf/rounds/new/RecentCoursesQuickPick.tsx
  • src/components/golf/settings/JoinTeamSection.tsx
  • src/components/golf/stats/GolfStatsDisplay.tsx
  • src/components/golf/stats/sections/shared-primitives.tsx
  • src/components/golf/tasks/CreateFromTemplateModal.tsx
  • src/components/golf/tasks/CreateTaskModal.tsx
  • src/components/golf/tasks/ReminderPicker.tsx
  • src/components/golf/tasks/TaskTemplateList.tsx
  • src/components/golf/travel/ExpenseForm.tsx
  • src/components/golf/travel/ExpenseList.tsx
  • src/components/golf/travel/ExpenseSummary.tsx
  • src/components/messages/NewMessageModal.tsx
  • src/components/player/VideoShowcase.tsx
  • src/components/recruiting/USStateMap.tsx
  • src/components/ui/checkbox.tsx
  • src/components/ui/confirm-dialog.tsx
  • src/components/ui/input.tsx
  • src/components/ui/page-header.tsx
  • src/components/ui/select.tsx
  • src/components/video/VideoClipper.tsx

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
  • Commit unit tests in branch fix/a11y-lint-batch

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 21c7bcb6be

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +198 to 200
<button
type="button"
onClick={handleClear}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep the clear action outside the select trigger button

When a clearable Select already has a value, this renders the clear <button> inside the trigger <button> that starts at line 167. Nested buttons are invalid HTML and can cause Next.js hydration mismatches while also producing unreliable keyboard/focus behavior; MultiSelect repeats the same structure at line 422. Use a non-button trigger container or position the clear button as a sibling.

Useful? React with 👍 / 👎.

Comment on lines +431 to +432
<button
type="button"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Do not make the interactive CRM card a button

On the mobile CRM view, this outer button contains a checkbox, favorite and menu buttons, status controls, and a mailto: link. Those nested interactive elements make the server-rendered markup invalid, which can trigger hydration recovery and prevents assistive technology from exposing the card controls reliably. Keep a non-interactive card container and provide a separate button or link for opening the coach.

Useful? React with 👍 / 👎.

Comment on lines +89 to +90
<button
type="button"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep the invite dialog outside the backdrop button

Whenever the invite modal opens, the backdrop button contains the entire dialog, including form controls and action buttons. Interactive descendants inside a button are invalid and give the dialog incorrect button semantics, leading to unreliable focus and keyboard activation. Render the backdrop as a sibling button or retain a non-interactive wrapper with a separate close control.

Useful? React with 👍 / 👎.

Comment on lines +276 to 280
<button
type="button"
aria-label="Close receipt viewer"
className="fixed inset-0 bg-warm-900/70 backdrop-blur-sm flex items-center justify-center z-50 p-4 w-full border-none cursor-default"
onClick={() => setViewingReceipt(null)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Separate the receipt backdrop from the modal controls

When a receipt is viewed, this button wraps the modal's close IconButton and embedded receipt content. That creates invalid nested interactive content and causes the whole dialog to be exposed as a close button, which can break keyboard and screen-reader navigation. Make the backdrop a sibling of the modal rather than its interactive parent.

Useful? React with 👍 / 👎.

Comment on lines +91 to 93
<button
type="button"
onClick={handleClick}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid wrapping the compact compare control in a button

When the compact card is rendered with showCheckbox, this new outer button contains the comparison Button at lines 105–117. The resulting nested buttons are invalid and can cause hydration or keyboard activation problems, including the card action competing with the comparison action. Keep the card container non-interactive and add a dedicated profile-opening control.

Useful? React with 👍 / 👎.

@devin-ai-integration devin-ai-integration 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.

Devin Review found 6 potential issues.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment on lines 305 to +308
</div>
)}
</div>
</div>
</button>

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.

🔴 Nested buttons in CalendarView monthly grid: day-cell buttons contain event-pill buttons

The monthly calendar view converts day cells from <div> to <button> (line 260), but each day cell contains event pill <button> elements as children (line 284). The HTML spec states that <button> elements must not contain interactive content. Browsers handle nested buttons inconsistently — Chrome may forcibly close the outer button before the inner one, breaking layout; Firefox may make inner buttons unclickable. This affects all three calendar views (month, week, day) where the same pattern was applied.

Affected nesting chains

Month view (line 260→284): outer day <button> → inner event <button>
Week view (line 401): event <button> is absolutely positioned inside a <div> sibling to hour-slot <button>s — this one is fine since they're siblings.
Day view (line 503): same sibling pattern — also fine.

The month view is the actual problem because event pills are direct descendants of the day-cell button.

(Refers to lines 260-308)

Prompt for agents
In CalendarView.tsx monthly view, the day cell was converted from a div to a button (line 260), but it contains nested event-pill buttons (line 284) as children. Buttons cannot contain other buttons per HTML spec. The fix is to revert the outer day cell back to a div and instead add role="button", tabIndex={0}, and an onKeyDown handler (Enter/Space) to the day cell div. Alternatively, restructure so the clickable day area and the event pills are siblings rather than parent-child. The event pills at line 284 can remain as buttons since they use stopPropagation. The week view and day view don't have this problem since their event buttons are absolutely positioned siblings, not children of the hour-slot buttons.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines 588 to 594
<IconChevronDown size={12} className="opacity-50" />
</Button>
{isStatusOpen && (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -- stopPropagation-only wrapper prevents card click from closing status dropdown
<div className="absolute z-50 mt-1 py-1 min-w-[160px] max-h-[320px] overflow-y-auto bg-white/95 backdrop-blur-xl rounded-xl border border-warm-200/50 shadow-xl" onClick={e => e.stopPropagation()}>
{ALL_STATUSES.map(status => (
<Button variant="primary"

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.

🔴 Nested interactive elements: MobileCoachCard button wraps checkboxes, buttons, and dropdown menus

The MobileCoachCard component was converted from a <div> to a <button> at line 431, but it contains multiple nested interactive elements: <input type="checkbox"> (line 445-449), <IconButton> star toggle (line 462), <IconButton> more-options (line 471), status <Button> dropdown trigger (line 578), and dropdown <Button> items (line 594). While e.stopPropagation() wrappers exist to prevent event bubbling, the HTML is invalid — <button> cannot contain interactive content. This causes unpredictable behavior across browsers and breaks screen reader navigation of the card's internal controls.

(Refers to lines 431-641)

Prompt for agents
In CoachTable.tsx, the MobileCoachCard was converted from div to button at line 431, but it contains nested checkboxes, IconButtons, Buttons, and dropdown menus. Buttons cannot contain interactive content per HTML spec. Revert back to a div with role="button", tabIndex={0}, and an onKeyDown handler for Enter/Space. The existing onClick={handleCardClick} and stopPropagation wrappers on inner controls can remain. This preserves keyboard accessibility without creating invalid nested interactive elements.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines 234 to +237
</Button>
</div>
</div>
</div>
</button>

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.

🔴 Modal backdrop as wraps nested Button components (InvitePlayerButton)

The modal backdrop in InvitePlayerButton.tsx was changed from <div> to <button> (line 89), but the modal content inside contains multiple <Button> components: 'Try Again' (line 162-168), 'Copy Invite Link' (line 191-207), and 'Close' (line 232). This creates invalid nested interactive HTML. Unlike the BaseballInviteButton.tsx in the same PR which correctly uses role="presentation" on the backdrop <div>, the golf version wraps everything in a <button>.

(Refers to lines 89-237)

Prompt for agents
In InvitePlayerButton.tsx, the modal backdrop was changed to a <button> at line 89, but it wraps the entire modal dialog including other Button components. This creates nested interactive elements. Follow the pattern used by BaseballInviteButton.tsx in this same PR: use a <div> with role="presentation", add onKeyDown for Escape, and give the inner modal div role="dialog" with aria-modal="true". The backdrop click is already handled by the e.target === e.currentTarget check.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines 310 to +313
)}
</div>
</motion.div>
</div>
</button>

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.

🔴 Receipt viewer backdrop as wraps nested IconButton and iframe

In ExpenseList.tsx, the receipt viewer modal backdrop was converted from <div> to <button> (line 276), but it contains a motion.div child with an <IconButton> (line 290) and an <iframe> (line 299-302) — both interactive content. The <button> cannot contain these elements per HTML spec. This will cause the close IconButton and iframe to behave unpredictably across browsers.

(Refers to lines 276-313)

Prompt for agents
In ExpenseList.tsx line 276, the receipt viewer backdrop was changed to a <button> but wraps interactive children (IconButton at line 290 and iframe at line 299). Revert to a <div> and use role="presentation" or role="dialog" with an onKeyDown for Escape. The onClick handler at line 280 to close the viewer can stay on the div. Alternatively, separate the backdrop button and the modal content as siblings (the pattern used in MobileEventSheet.tsx in this same PR).
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines 170 to +173
<div className="text-warm-400">
{isExpanded ? <IconChevronUp size={18} /> : <IconChevronDown size={18} />}
</div>
</div>
</button>

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.

🟡 Expense row expand/collapse button wraps interactive child buttons

In ExpenseList.tsx, the expense row clickable area was changed from <div> to <button> at line 128-130, but when expanded, the AnimatePresence section rendered below it (starting at line 175) contains interactive <IconButton> elements for edit and delete actions. While the expanded content is technically a sibling in the motion wrapper, the outer <button> at line 128 does not self-close before the expanded region — the expanded section is rendered after the </button> at line 173. Let me re-verify... Actually, looking again: the <button> closes at line 173, and the AnimatePresence is a sibling. This is actually fine. Disregard.

(Refers to lines 128-173)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +63 to +69
<div
className="fixed inset-0 bg-warm-900/50 backdrop-blur-sm z-50 flex items-center justify-center p-4"
role="button"
tabIndex={0}
aria-label="Close"
onClick={onClose}
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') onClose(); }}

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.

🚩 PlayerDetailModal uses role="button" on backdrop div containing interactive Card

The PlayerDetailModal.tsx backdrop <div> was given role="button" and tabIndex={0} at src/components/coach/PlayerDetailModal.tsx:65-66. While role="button" doesn't enforce HTML content model restrictions like a real <button> would, it still semantically declares the entire overlay (including the modal Card with close button, watchlist button, and message button) as a single button to assistive technology. Screen readers will announce the entire modal region as a button. The BaseballInviteButton.tsx pattern (role="presentation") would be more semantically correct for a backdrop overlay.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@njrini99-code

Copy link
Copy Markdown
Owner Author

Folded into #304 (feat/coachhelm-stats-roundup) — merged clean, combined gates green. Branch intact + reopenable.

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.

1 participant