Skip to content

Fix focus trap: focus textarea on open and restore focus on close - #486

Open
onyekachi66 wants to merge 1 commit into
TevaLabs:mainfrom
onyekachi66:focus-trap-fix
Open

Fix focus trap: focus textarea on open and restore focus on close#486
onyekachi66 wants to merge 1 commit into
TevaLabs:mainfrom
onyekachi66:focus-trap-fix

Conversation

@onyekachi66

Copy link
Copy Markdown

Closes #427

TL;DR

When the mobile chat sidebar (implemented as a bottom‑sheet) opens, the input textarea should receive focus automatically. When the sheet is closed (via Escape or overlay click) focus must be restored to the button that opened the sheet. This PR adds the missing initialFocusRef to the existing useFocusTrap hook usage, preserving the existing tab‑cycling behavior and ensuring a smooth keyboard‑only experience.


Background

The mobile chat UI is displayed as a bottom‑sheet (<aside>).
Current behavior:

Action Current Result
Open sheet (tap toggle) Sheet appears, but focus remains on the toggle button. Users must click the textarea before typing.
Tab navigation Works, but focus starts on the toggle button, which is outside the sheet.
Escape key Closes sheet and does restore focus to the toggle button (via restoreFocusRef).

Issue: The spec requires that focus immediately lands on the textarea when the sheet opens, and that focus is restored correctly on close. The useFocusTrap hook already supports an initialFocusRef prop, but it was never supplied.


Changes Made

File Line(s) Change
src/components/ChatSidebar.tsx 160‑165 Updated the useFocusTrap call to include:
tsx\nuseFocusTrap(sidebarRef, {\n active: isMobileOpen,\n onEscape: () => setIsMobileOpen(false),\n initialFocusRef: textareaRef, // 👈 new\n restoreFocusRef: mobileToggleRef,\n});\n
All other logic remains unchanged.

No other files were touched.
The only functional addition is passing textareaRef (the ref for the <textarea> input) as initialFocusRef.


Implementation Details

  1. useFocusTrap Hook (src/hooks/useFocusTrap.ts):

    • Accepts initialFocusRef?: RefObject<HTMLElement> and focuses that element when the trap becomes active.
    • The hook already restores focus to restoreFocusRef on cleanup.
  2. Component Integration (ChatSidebar):

    • textareaRef is already defined (useRef<HTMLTextAreaElement>(null)).
    • By supplying it to the hook, the textarea receives focus on the next animation frame (window.setTimeout(..., 0)) as soon as the sheet is rendered and isMobileOpen becomes true.
    • The existing restoreFocusRef: mobileToggleRef continues to return focus to the toggle button when the sheet is closed.
  3. No New Dependencies – The hook and refs were already present; we only wired them together.


Verification / Testing Plan

Automated (manual verification)

  1. Run the app in a mobile viewport (or use Chrome DevTools device emulation).
  2. Click the chat toggle button (bottom‑right floating button).
    • Expected: The textarea is immediately focused (cursor visible, ready to type).
  3. Press Tab repeatedly.
    • Expected: Focus cycles only among elements inside the sheet (textarea, send button, etc.) and never escapes to the underlying page.
  4. Press Shift + Tab at the first focusable element.
    • Expected: Focus jumps to the last focusable element inside the sheet.
  5. Press Escape or click the dark overlay.
    • Expected: Sheet closes and focus returns to the toggle button.

Impact Assessment

  • User Experience: Improves keyboard‑only navigation on mobile, aligning with accessibility best practices (WCAG 2.1 Success Criterion 2.1.1 – Keyboard).
  • Performance: No measurable impact; only an extra focus() call on mount.
  • Compatibility: Works across all browsers that support focus() on textarea elements; uses the existing useFocusTrap implementation that already handles key events.
  • Risk: Minimal – changes are confined to a single hook call with no side‑effects.

Related Issues / Tickets

  • Issue: “Focus composer on open; restore trigger on close” (internal tracking).
  • Acceptance Criteria:
    • Tab cycles within sheet – satisfied by existing focus‑trap logic.
    • Escape closes and restores focus – already handled; unchanged.

Checklist

  • Code builds (npm run lint && npm run build – passes).
  • Local test on mobile viewport confirms behavior.
  • No lint warnings introduced.
  • Documentation/comments updated where necessary (no new comments needed).

@vercel

vercel Bot commented Aug 26, 2026

Copy link
Copy Markdown

@onyekachi66 is attempting to deploy a commit to the josephchimebuka's projects Team on Vercel.

A member of the Team first needs to authorize it.

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.

Fix ChatSidebar focus order when opening on mobile

1 participant