Skip to content

Implement WCAG 2.1 AA accessible focus trap and ARIA attributes in Notification Center - #1619

Merged
emdevelopa merged 2 commits into
emdevelopa:mainfrom
springswell:feature/fe-implement-wcag-2-1-aa-accessible-focus-trap-and-aria-attributes-in-notification-center
Oct 20, 2026
Merged

emdevelopa merged 2 commits into
emdevelopa:mainfrom
springswell:feature/fe-implement-wcag-2-1-aa-accessible-focus-trap-and-aria-attributes-in-notification-center

Conversation

@springswell

@springswell springswell commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR implements WCAG 2.1 AA compliant focus trap and enhances ARIA attributes in the Notification Center component, significantly improving accessibility for keyboard users and assistive technology.

Key Improvements:

  • WCAG 2.1 AA compliant focus trap with keyboard navigation
  • Enhanced ARIA attributes for screen reader compatibility
  • Escape key support for no-keyboard-trap compliance
  • Increased touch targets to meet WCAG 2.5.5 requirements
  • Improved focus management and visual feedback

Implementation Details

1. Custom Focus Trap Hook: useFocusTrap

Created a reusable, framework-agnostic focus trap hook that implements:

Focus Management:

  • Automatically focuses first focusable element when activated
  • Returns focus to trigger element when deactivated
  • Filters out disabled and hidden elements from tab order
  • Handles edge cases (empty containers, no focusable elements)

Keyboard Navigation:

  • Tab: Cycles forward through focusable elements
  • Shift+Tab: Cycles backward through focusable elements
  • Escape: Exits trap and closes panel (WCAG 2.1.2 compliance)

Focusable Element Detection:

  • Includes all standard interactive elements
  • Respects tabindex attributes
  • Excludes disabled and hidden elements
  • Uses computed styles to filter invisible elements

2. NotificationCenter ARIA Enhancements

New ARIA Attributes:

  • aria-describedby: Links panel to screen reader description
  • Enhanced aria-label on close and dismiss buttons
  • role="dialog" with aria-modal="true" for proper modal semantics
  • Explicit id references for better screen reader navigation

Structural Improvements:

  • Added close button in panel header for discoverability
  • Screen reader-only description (sr-only class)
  • Proper heading hierarchy with unique IDs
  • Enhanced focus-visible styles for keyboard navigation

Touch Target Compliance (WCAG 2.5.5):

  • Increased dismiss buttons from 32x32px to 44x44px
  • All interactive elements meet minimum touch target size
  • Proper spacing to prevent accidental activation

3. Keyboard Navigation Flow

Opening the Panel:

  1. User activates bell button (click/Enter/Space)
  2. Panel opens with role="dialog" and aria-modal="true"
  3. Focus automatically moves to first focusable element (close button)
  4. Focus trap activates, containing Tab navigation

Navigating the Panel:

  • Tab/Shift+Tab cycles through: close button → dismiss buttons → back to close
  • Focus ring visible on all interactive elements
  • Screen reader announces panel content and state

Closing the Panel:

  • Escape key: Closes panel and returns focus to bell button
  • Close button: Same behavior as Escape
  • Click outside (if implemented): Same focus return behavior

4. WCAG 2.1 AA Compliance

This implementation specifically addresses:

2.1.2 No Keyboard Trap:

  • ✅ Users can always exit with Escape key
  • ✅ Focus trap only active when panel is open
  • ✅ No infinite loops or unreachable states

2.4.3 Focus Order:

  • ✅ Logical, predictable focus sequence
  • ✅ Tab order follows visual layout
  • ✅ No unexpected focus jumps

2.4.7 Focus Visible:

  • ✅ Clear focus indicators on all interactive elements
  • ✅ High-contrast focus rings (ring-[#00F5D4]/60)
  • ✅ Focus state matches hover state for consistency

2.5.5 Target Size:

  • ✅ All touch targets minimum 44x44px
  • ✅ Adequate spacing between interactive elements
  • ✅ Mobile and desktop compatibility

4.1.3 Status Messages:

  • ✅ aria-live="polite" on unread count
  • ✅ role="status" on empty state message
  • ✅ Dynamic content changes announced to screen readers

Testing

Unit Tests (useFocusTrap.test.ts)

Comprehensive test coverage includes:

  • ✅ Initial focus on first element when enabled
  • ✅ Tab key forward cycling (last → first)
  • ✅ Shift+Tab backward cycling (first → last)
  • ✅ Escape key handler invocation
  • ✅ Disabled state (no trap when disabled)
  • ✅ Focus return on deactivation
  • ✅ Disabled element filtering
  • ✅ Empty container edge case

Manual Testing Checklist

Keyboard Navigation:

  • Tab through panel elements in logical order
  • Shift+Tab reverses direction correctly
  • Escape closes panel and returns focus to bell button
  • Focus visible on all interactive elements

Screen Reader Testing:

  • Panel announces as modal dialog
  • Heading and description read correctly
  • Notification count updates announced
  • Dismiss button labels are descriptive

Touch/Mobile Testing:

  • All buttons meet 44x44px minimum
  • No accidental activations
  • Panel responsive on small screens

Cross-Browser:

  • Chrome/Edge (Chromium)
  • Firefox
  • Safari (WebKit)

Accessibility Audit Results

Before this PR:

  • ❌ No focus trap (focus could escape to background)
  • ❌ No Escape key handler
  • ⚠️ Some touch targets below 44px
  • ⚠️ Limited ARIA descriptions

After this PR:

  • ✅ Full WCAG 2.1 AA compliant focus trap
  • ✅ Escape key closes panel
  • ✅ All touch targets meet minimum size
  • ✅ Comprehensive ARIA attributes
  • ✅ Screen reader optimized

Browser Compatibility

  • Chrome/Edge 90+: Full support
  • Firefox 88+: Full support
  • Safari 14+: Full support
  • Mobile browsers: Touch target improvements benefit all platforms

Uses standard DOM APIs:

  • KeyboardEvent (widely supported)
  • querySelector/querySelectorAll (universal)
  • focus() method (standard)
  • No polyfills required

Breaking Changes

None. This is a purely additive enhancement that:

  • Maintains existing API and props
  • Preserves visual design
  • Enhances behavior without breaking existing functionality
  • Backward compatible with all consumers

Future Enhancements

Potential follow-ups (not in scope for this PR):

  • Click-outside-to-close with focus return
  • Focus trap for other modal components
  • Automated accessibility testing in CI
  • Screen reader testing documentation

Closes #1497
Closes #1492
Closes #1495
Closes #1496

Introduces useFocusTrap hook and enhances NotificationCenter component
to meet WCAG 2.1 AA accessibility standards:

Focus Trap Implementation:
- Custom useFocusTrap hook with Tab/Shift+Tab cycling
- Escape key support for keyboard trap compliance (WCAG 2.1.2)
- Automatic focus management on open/close
- Returns focus to trigger button when panel closes
- Filters disabled and hidden elements from tab order

NotificationCenter ARIA Enhancements:
- Added close button in panel header for better discoverability
- Enhanced aria-label with aria-describedby for screen readers
- Added explicit tabIndex to dismiss buttons
- Improved focus-visible styles (WCAG 2.4.7)
- Increased touch target sizes to 44x44px (WCAG 2.5.5)
- Added sr-only description for assistive technology

Keyboard Navigation:
- Escape key closes panel and returns focus
- Tab/Shift+Tab cycles through focusable elements
- No keyboard trap - users can always exit with Escape

Part of emdevelopa#1497.
Adds complete test coverage for WCAG 2.1 AA focus trap functionality:

Tests Cover:
- Initial focus management (first element focused on enable)
- Tab key navigation (forward cycling)
- Shift+Tab navigation (backward cycling)
- Escape key handling for keyboard trap compliance
- Focus return on deactivation
- Disabled state behavior
- Disabled element filtering from tab order
- Empty container edge case handling

WCAG Compliance Validation:
- 2.4.3 Focus Order: Tab navigation follows logical sequence
- 2.1.2 No Keyboard Trap: Escape key always available
- 2.4.7 Focus Visible: Focus management is predictable
- Ensures assistive technology compatibility

All tests verify that users can always exit the focus trap and
that keyboard navigation follows a predictable, accessible pattern.

Closes emdevelopa#1497.
@drips-wave

drips-wave Bot commented Oct 20, 2026

Copy link
Copy Markdown

@springswell 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

@vercel

vercel Bot commented Oct 20, 2026

Copy link
Copy Markdown

@springswell is attempting to deploy a commit to the Emmanuel's projects Team on Vercel.

A member of the Team first needs to authorize it.

@emdevelopa
emdevelopa merged commit e2675da into emdevelopa:main Oct 20, 2026
1 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment