Skip to content

feat(a11y,mobile): implement mobile nav drawer and a11y improvements - #143

Merged
therealjhay merged 2 commits into
Betta-Pay:mainfrom
privexlabs:feat/mobile-nav-a11y-improvements
Jun 29, 2026
Merged

feat(a11y,mobile): implement mobile nav drawer and a11y improvements#143
therealjhay merged 2 commits into
Betta-Pay:mainfrom
privexlabs:feat/mobile-nav-a11y-improvements

Conversation

@privexlabs

Copy link
Copy Markdown
Contributor

Summary

Changes

#18 — Mobile navigation drawer

  • New components/layout/MobileNavDrawer.tsx: framer-motion slide-in from left, semi-transparent
    backdrop, visible close (X) button, Escape key, body scroll lock, role="dialog", aria-modal="true",
    aria-label="Navigation", aria-current="page" on active link
  • app/(merchant)/layout.tsx: wired drawer to existing mobileMenuOpen state with useCallback to prevent
    stale closure in Escape handler
  • components/layout/MerchantSidebar.tsx: exported navItems so the drawer reuses the same nav structure
  • components/layout/Topbar.tsx: added aria-controls="mobile-nav" to hamburger button

#17 — Color not sole means of conveying information

  • components/layout/MerchantSidebar.tsx: added aria-current="page" on the active nav link (screen readers
    announce this natively)
  • components/layout/Topbar.tsx: fixed unreadNotificationCount prop not being destructured; notification
    button aria-label now uses the computed notificationLabel (e.g. "Notifications (3 unread)"); visual red
    dot marked aria-hidden="true"
  • components/shared/StatusBadge.tsx: already compliant — icon + text label for all states, no change needed

#16aria-live region for dynamic content

  • New lib/utils/announce.ts: minimal DOM-write utility (clears then sets with 100ms delay so AT can
    re-announce)
  • app/layout.tsx: added <div id="announcer" aria-live="polite" aria-atomic="true" className="sr-only" />
    always in DOM, never conditionally mounted
  • lib/hooks/useNotify.ts: calls announce(message) alongside each toast.* call
  • app/(merchant)/transactions/page.tsx: added <p role="status" className="sr-only"> with live result count
    that updates as the search term changes

#15 — Focus trap in payment link dialog

  • app/(merchant)/payments/page.tsx: added autoFocus to the label input so initial focus is explicit when
    the dialog opens
  • @base-ui/react Dialog v1.5 handles focus trapping, Tab cycling, Escape to close, and focus-return to
    trigger natively — verified <Dialog.Close> is already rendered inside DialogContent

Test Plan

  • Mobile (<768px): tap hamburger → drawer slides in from left; tap backdrop → closes; tap X → closes;
    press Escape → closes; tap any nav link → navigates and drawer closes; page does not scroll behind drawer
  • Desktop (≥768px): drawer and backdrop are not visible; sidebar behaves as before
  • Screen reader (VoiceOver): trigger a toast → announcement heard via live region; type in Transactions
    search → result count announced; active sidebar link reads "current page"
  • Keyboard: open payment link dialog → focus lands on Label input; Tab cycles only within dialog; Escape
    closes and returns focus to "New Payment Link" button
  • DOM inspection: active sidebar link has aria-current="page"; notification button aria-label reflects
    unread count; red dot has aria-hidden="true"

Resolves #15
Resolves #16
Resolves #17
Resolves #18

- Betta-Pay#18: Add MobileNavDrawer with framer-motion slide animation, backdrop,
  Escape key, close button, body scroll lock, and ARIA dialog attributes.
  Wire to existing mobileMenuOpen state in merchant layout.
- Betta-Pay#17: Add aria-current="page" on active sidebar links; fix unreadNotificationCount
  destructure in Topbar; use dynamic notificationLabel on bell button aria-label;
  mark visual notification dot aria-hidden.
- Betta-Pay#16: Add global aria-live polite announcer div in root layout; create
  announce utility; wire useNotify to announce toasts; add role="status"
  result count in transactions page.
- Betta-Pay#15: Add autoFocus to payment link dialog label input; @base-ui/react
  Dialog handles focus trap, Escape, and focus-return natively.
- Fix TransactionDetail syntax error (useNotify inside param list).
- Fix PaymentsPage missing notify initialization.

Resolves Betta-Pay#15, Resolves Betta-Pay#16, Resolves Betta-Pay#17, Resolves Betta-Pay#18
@vercel

vercel Bot commented Jun 28, 2026

Copy link
Copy Markdown

@privexlabs is attempting to deploy a commit to the therealjhay's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Jun 28, 2026

Copy link
Copy Markdown

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

@therealjhay
therealjhay merged commit 2f8969d into Betta-Pay:main Jun 29, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants