Skip to content

fix: make the exit confirmation an accessible dialog#974

Open
Anexus5919 wants to merge 1 commit into
Somil450:mainfrom
Anexus5919:fix/accessible-exit-modal
Open

fix: make the exit confirmation an accessible dialog#974
Anexus5919 wants to merge 1 commit into
Somil450:mainfrom
Anexus5919:fix/accessible-exit-modal

Conversation

@Anexus5919

Copy link
Copy Markdown
Contributor

📌 Related Issue

Fixes #891


📝 Description

The "Confirm Exit" modal, duplicated in App.tsx and WorkoutScreen.tsx, was built from plain <div>s: no dialog role, no focus management or trap, no Escape to close, and (in App) a "Stay" button with no styling that rendered low-contrast.

🔹 What has been changed?

  • New reusable ExitConfirmModal with role="dialog", aria-modal="true", and aria-labelledby (id from useId); focus moved to the non-destructive Stay button on open; Tab focus trapped within the dialog (wraps both ends); Escape closes (invokes Stay); focus restored to the previously focused element on close; and the app's btn-outline (Stay) / btn-neon + red (Exit) classes for adequate, theme-aware contrast.
  • Replaced the inline modal in both App.tsx and WorkoutScreen.tsx with <ExitConfirmModal> (preserving each one's message and exit action).

🔹 Why are these changes needed?

  • A confirm dialog should be announced as a dialog, trap focus, close on Escape, and restore focus, none of which the plain-div version did. Extracting one accessible component fixes both copies and gives the "Stay" button proper contrast.

🛠️ Type of Change

  • 🐛 Bug Fix
  • 🎨 UI/UX Improvement

🧪 Testing

✅ Tests Performed

  • Tested locally
  • npx tsc --noEmit clean; npx eslint clean for the change (the pre-existing user?.uid exhaustive-deps warning on an unrelated WorkoutScreen effect is unchanged).
  • Traced the controlled focus flow (open -> focus Stay, Tab traps, Escape -> Stay, close -> restore focus).

🔹 Note on automated tests

No committed test: the focus-trap/Escape behavior needs @testing-library/react, which can't currently run (@testing-library/dom evicted from node_modules).

🌐 Browsers Tested

Not applicable here (focus/dialog behavior; verified via reasoning and the Accessibility pane).


📷 Screenshots / Demo (if applicable)

A capture comparing the Stay/Exit button contrast and showing Tab staying within the modal would help.


📋 Checklist

  • I have read the project's CONTRIBUTING guidelines
  • My code follows the project style guidelines
  • I have performed a self-review of my code
  • I have tested my changes locally
  • I have added/updated documentation where necessary (not applicable)
  • My changes do not introduce new warnings or errors
  • This PR is linked to an existing issue

💬 Additional Notes

Branched from upstream/main; merges cleanly with the current base. Fixes both duplicated copies of the modal (App and WorkoutScreen) via one shared component.

@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown

@Anexus5919 is attempting to deploy a commit to the somiljain2024-4175's projects Team on Vercel.

A member of the Team first needs to authorize it.

@Anexus5919

Copy link
Copy Markdown
Contributor Author

@Somil450 @diksha78dev Kindly have a review on this pr. Thanks!

The exit-confirm modal (duplicated in App and WorkoutScreen) was plain
divs: no dialog role, no focus management or trap, no Escape to close, and
App's "Stay" button had no styling so it rendered with low contrast.

Extract a reusable ExitConfirmModal with role="dialog", aria-modal, and
aria-labelledby; move focus to the safe "Stay" action on open, trap Tab
within the dialog, close on Escape, and restore focus to the trigger on
close. Use the app's btn-outline/btn-neon classes so both buttons have
adequate contrast. Render it from both App and WorkoutScreen.
@Anexus5919 Anexus5919 force-pushed the fix/accessible-exit-modal branch from 26d6cfb to de859e5 Compare July 1, 2026 07:21
@Anexus5919

Copy link
Copy Markdown
Contributor Author

@Somil450 @diksha78dev Kindly have a review on this pr. Thanks!

@Anexus5919

Copy link
Copy Markdown
Contributor Author

This failing check is from main, not this PR. Main's CI is currently broken (ShieldAlert used without importing it, plus untyped "privacy"/"terms&conditions" screens), so every PR goes red, even docs-only ones. Fix is up in #1039. Once it merges, a rebase turns this green. Merges cleanly with base.

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.

Exit confirmation modal is not an accessible dialog (no role/focus-trap/Escape; low-contrast Stay)

1 participant