Skip to content

fix: add escape-to-dismiss and focus pause to toast notifications#383

Open
edrizxabdulganiyu-blip wants to merge 2 commits into
Liquifact:mainfrom
edrizxabdulganiyu-blip:a11y/toast-22-keyboard-dismiss
Open

fix: add escape-to-dismiss and focus pause to toast notifications#383
edrizxabdulganiyu-blip wants to merge 2 commits into
Liquifact:mainfrom
edrizxabdulganiyu-blip:a11y/toast-22-keyboard-dismiss

Conversation

@edrizxabdulganiyu-blip

Copy link
Copy Markdown

fix: keyboard accessibility for toast notifications

Summary

Toast notifications were only dismissible by clicking "Close" or waiting for the timer. A
keyboard user focusing a toast had no way to dismiss it, and the timer was not paused on focus
— so the toast could vanish while being read. This PR brings full keyboard parity with the
existing mouse behaviour.

Changes

components/ToastProvider.jsx

  • tabIndex={0} on each toast card — makes cards reachable via Tab without requiring the user to
    navigate to the Close button first.
  • onFocus → pause timer — mirrors the existing onMouseEnter pause. Records the
    previously-focused element so focus can be restored after dismissal.
  • onBlur → resume timer — mirrors the existing onMouseLeave resume. Only resumes when focus
    leaves the entire toast region; moving focus between the card and its Close button keeps the
    timer paused.
  • onKeyDown Escape → dismiss — pressing Escape on a focused toast (or its Close button)
    dismisses that toast only.
  • Focus return — after any dismissal (Escape or Close button click), focus is restored to the
    element that was active before the user tabbed into the toast, preventing focus loss to .

components/ToastProvider.keyboard.test.tsx (new — 14 tests)

┌─────┬──────────┐
│ # │ Scenario │
├─────┼────────────────────────────┤
│ 1 │ tabIndex=0 present on card │
├─────┼─────────────────────────────────┤
│ 2 │ Focus pauses auto-dismiss timer │
├─────┼──────────────────────────────────────┤
│ 3 │ Blur outside container resumes timer │
├─────┼─────────────────────────────────────────────────────┤
│ 4 │ Focus moving card ↔ Close button keeps timer paused │
├─────┼─────────────────────────────────────────────────────┤
│ 5 │ Escape on card dismisses it │
├─────┼─────────────────────────────────────────────────────┤
│ 6 │ Escape on Close button dismisses it │
├─────┼─────────────────────────────────────────────────────┤
│ 7 │ Focus returns to pre-toast element after Escape │
├─────┼──────────────────────────────────────────────────────┤
│ 8 │ Focus returns to pre-toast element after Close click │
├─────┼──────────────────────────────────────────────────────┤
│ 9 │ No jest-axe violations when toast is focused │
├─────┼──────────────────────────────────────────────────────┤
│ 10 │ Escape only dismisses the focused toast, not all │
├─────┼──────────────────────────────────────────────────────┤
│ 11 │ Non-Escape keys (Enter, Space, Tab) do not dismiss │
├─────┼──────────────────────────────────────────────────────┤
│ 12 │ Escape-dismiss cancels the stale timer │
├─────┼──────────────────────────────────────────────────────┤
│ 13 │ Hover pause and focus pause coexist correctly │
├─────┼──────────────────────────────────────────────────────┤
│ 14 │ Card is reachable via userEvent Tab traversal │
└─────┴──────────────────────────────────────────────────────┘

README.md

Added a Toast notification keyboard behaviour table under the Accessibility section.

What was tested

  • All 24 ToastProvider tests pass (14 new keyboard + 7 original + 3 dedupe).
  • Existing ToastProvider.test.tsx and ToastProvider.dedupe.test.tsx pass unchanged.
  • No jest-axe violations.
  • Pre-existing build/lint failures (UploadZone.jsx duplicate name, missing eslint-plugin-react)
    are unrelated to this PR.

Accessibility contract

- Add tabIndex=0 to toast cards so they are Tab-reachable
- Pause auto-dismiss on focus (mirrors mouseenter), resume on blur (mirrors
  mouseleave) — keyboard users can read a toast without it vanishing
- Dismiss the focused toast on Escape key press
- Restore focus to the pre-toast element after any dismissal (Escape or
  Close button) to prevent focus loss to <body>
- Add containerRef to detect focus entering/leaving the toast region so
  intra-toast focus movement (card ↔ Close button) does not restart timer

Tests (components/ToastProvider.keyboard.test.tsx, 14 tests):
  - tabIndex=0 presence
  - focus pauses timer, blur resumes timer
  - intra-toast focus movement keeps timer paused
  - Escape dismisses focused card or Close button
  - focus return after Escape and after Close click
  - no jest-axe violations when focused
  - Escape only dismisses the focused toast
  - non-Escape keys do not dismiss
  - hover and focus pauses coexist correctly
  - userEvent Tab traversal reaches the card

Docs: add Toast notification keyboard behaviour table to README.md
@drips-wave

drips-wave Bot commented Jul 1, 2026

Copy link
Copy Markdown

@edrizxabdulganiyu-blip 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

@edrizxabdulganiyu-blip

Copy link
Copy Markdown
Author

can you please drop point

omolobamoyinoluwa-max pushed a commit to omolobamoyinoluwa-max/Liquifact-frontend that referenced this pull request Jul 13, 2026
…ultisig-weight-threshold-263-gas-optimized-health

feat: Liquifact#264 multisig weight threshold + Liquifact#263 gas-optimized health slots
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.

Give the toast close button reliable Escape-to-dismiss and focus handling

1 participant