From 862a128102c343985f7fa03aca96732ac58b6dd9 Mon Sep 17 00:00:00 2001 From: Lotus CI Date: Thu, 24 Sep 2026 12:15:02 -0400 Subject: [PATCH] fix(a11y): skip link focuses
; ? dialog and reaction viewer announce (#185) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Skip link: following #main-content left focus on (the
target wasn't focusable), so nothing was announced and the URL got a stray fragment.
is now tabIndex=-1 and the link focuses it. - Keyboard-shortcuts dialog (?): no role, and focus stayed in the timeline, so it opened silently. Now role="dialog" aria-modal, and focus moves into it (Escape still returns focus to where you were). - Reaction viewer (both the reaction context-menu path and "View Reactions"): same — role="dialog" aria-modal aria-label="Reactions", focus moves in. Keyboard-only checks (Playwright): Tab → skip link → Enter focuses
; Tab reaches the room list, Enter opens a room, typing lands in the composer, Enter sends (verified on the server); focus ring visible. "?" opens the dialog with focus inside, Escape returns to the same element, "?" in the composer stays text. Topic viewer, Search dialog and reaction viewer all return focus to their opener. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA --- src/app/features/room/message/Message.tsx | 15 +++++++++++++-- src/app/features/room/message/Reactions.tsx | 15 +++++++++++++-- .../shortcuts/KeyboardShortcutsDialog.tsx | 10 ++++++++-- src/app/pages/client/ClientLayout.tsx | 9 ++++++++- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/app/features/room/message/Message.tsx b/src/app/features/room/message/Message.tsx index a801934c3..34fb166ec 100644 --- a/src/app/features/room/message/Message.tsx +++ b/src/app/features/room/message/Message.tsx @@ -223,14 +223,25 @@ export const MessageAllReactionItem = as< handleClose(), clickOutsideDeactivates: true, escapeDeactivates: stopPropagation, }} > - + ( setViewer(false), clickOutsideDeactivates: true, escapeDeactivates: stopPropagation, }} > - +
{ (e.currentTarget as HTMLElement).style.top = '-40px'; }} + // #185 P1: following the fragment left focus on (the
target + // wasn't focusable), so screen readers announced nothing and the URL got + // a stray #main-content. Move focus to
explicitly instead. + onClick={(e) => { + e.preventDefault(); + document.getElementById('main-content')?.focus(); + }} > Skip to main content @@ -41,7 +48,7 @@ export function ClientLayout({ nav, children }: ClientLayoutProps) { {nav} - + {children} {bookmarksOpen && (