diff --git a/src/app/features/room/MediaGallery.tsx b/src/app/features/room/MediaGallery.tsx
index 44138016e..ff213cc7e 100644
--- a/src/app/features/room/MediaGallery.tsx
+++ b/src/app/features/room/MediaGallery.tsx
@@ -261,7 +261,6 @@ function Lightbox({
escapeDeactivates: false,
}}
>
- {}
(
[typingMembers, myUserId, room],
);
- if (typingNames.length === 0) {
- return null;
- }
-
// A single, non-truncated string for assistive technology to announce.
+ // Computed even when empty so the live region can stay mounted (below) —
+ // a `role="status"` region added to the DOM together with its first text
+ // is not reliably announced by some screen readers.
let typingAnnouncement = '';
if (typingNames.length === 1) {
typingAnnouncement = `${typingNames[0]} is typing`;
@@ -65,85 +64,88 @@ export const RoomViewTyping = as<'div', RoomViewTypingProps>(
return (
+ {/* Persistently mounted so the FIRST "X is typing" is announced. */}
{typingAnnouncement}
-
-
-
- {typingNames.length === 1 && (
- <>
- {typingNames[0]}
-
- {' is typing...'}
-
- >
- )}
- {typingNames.length === 2 && (
- <>
- {typingNames[0]}
-
- {' and '}
-
- {typingNames[1]}
-
- {' are typing...'}
-
- >
- )}
- {typingNames.length === 3 && (
- <>
- {typingNames[0]}
-
- {', '}
-
- {typingNames[1]}
-
- {' and '}
-
- {typingNames[2]}
-
- {' are typing...'}
-
- >
- )}
- {typingNames.length > 3 && (
- <>
- {typingNames[0]}
-
- {', '}
-
- {typingNames[1]}
-
- {', '}
-
- {typingNames[2]}
-
- {' and '}
-
- {typingNames.length - 3} others
-
- {' are typing...'}
-
- >
- )}
-
- 0 && (
+
-
-
-
+
+
+ {typingNames.length === 1 && (
+ <>
+ {typingNames[0]}
+
+ {' is typing...'}
+
+ >
+ )}
+ {typingNames.length === 2 && (
+ <>
+ {typingNames[0]}
+
+ {' and '}
+
+ {typingNames[1]}
+
+ {' are typing...'}
+
+ >
+ )}
+ {typingNames.length === 3 && (
+ <>
+ {typingNames[0]}
+
+ {', '}
+
+ {typingNames[1]}
+
+ {' and '}
+
+ {typingNames[2]}
+
+ {' are typing...'}
+
+ >
+ )}
+ {typingNames.length > 3 && (
+ <>
+ {typingNames[0]}
+
+ {', '}
+
+ {typingNames[1]}
+
+ {', '}
+
+ {typingNames[2]}
+
+ {' and '}
+
+ {typingNames.length - 3} others
+
+ {' are typing...'}
+
+ >
+ )}
+
+
+
+
+
+ )}
);
},
diff --git a/src/app/features/shortcuts/KeyboardShortcutsDialog.tsx b/src/app/features/shortcuts/KeyboardShortcutsDialog.tsx
index e89f2e9b9..c10c454f4 100644
--- a/src/app/features/shortcuts/KeyboardShortcutsDialog.tsx
+++ b/src/app/features/shortcuts/KeyboardShortcutsDialog.tsx
@@ -55,6 +55,10 @@ export function useKeyboardShortcutsTrigger() {
// `?` is produced by Shift + `/` on the common layouts.
if (evt.key === '?') {
evt.preventDefault();
+ // Stop RoomView's window-level "type any char → focus composer"
+ // handler from also firing — otherwise focus lands in the composer
+ // behind the dialog and Escape gets swallowed by the contenteditable.
+ evt.stopImmediatePropagation();
setOpen(true);
}
},