From 5427f5a2e3c13cecf9a4e57ea2ac8f5c7bb85dba Mon Sep 17 00:00:00 2001 From: automated-signal <37887102+automated-signal@users.noreply.github.com> Date: Tue, 9 Jun 2026 22:04:51 -0500 Subject: [PATCH] Autofocus on close button when opening lightbox Co-authored-by: trevor-signal <131492920+trevor-signal@users.noreply.github.com> --- stylesheets/components/Lightbox.scss | 6 ++++-- ts/components/Lightbox.dom.tsx | 7 ++----- ts/components/conversation/Message.dom.tsx | 1 + ts/hooks/useRestoreFocus.dom.ts | 7 ++++++- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/stylesheets/components/Lightbox.scss b/stylesheets/components/Lightbox.scss index 10376657df..ebcd97637e 100644 --- a/stylesheets/components/Lightbox.scss +++ b/stylesheets/components/Lightbox.scss @@ -332,8 +332,10 @@ width: 100%; } - &:focus { - outline: 4px solid variables.$color-ultramarine; + @include mixins.keyboard-mode { + &:focus { + outline: 4px solid variables.$color-ultramarine; + } } &:disabled { diff --git a/ts/components/Lightbox.dom.tsx b/ts/components/Lightbox.dom.tsx index 9a9297c823..08ed9e3c4f 100644 --- a/ts/components/Lightbox.dom.tsx +++ b/ts/components/Lightbox.dom.tsx @@ -729,11 +729,7 @@ export function Lightbox({ role="presentation" >
-
+
{getConversation && currentItem != null ? (
diff --git a/ts/components/conversation/Message.dom.tsx b/ts/components/conversation/Message.dom.tsx index cf0b32c709..de0d487e3f 100644 --- a/ts/components/conversation/Message.dom.tsx +++ b/ts/components/conversation/Message.dom.tsx @@ -3309,6 +3309,7 @@ export class Message extends PureComponent { return; } + window.enterKeyboardMode(); this.handleOpen(event); }; diff --git a/ts/hooks/useRestoreFocus.dom.ts b/ts/hooks/useRestoreFocus.dom.ts index ed1f443a75..b1b7d5e2f7 100644 --- a/ts/hooks/useRestoreFocus.dom.ts +++ b/ts/hooks/useRestoreFocus.dom.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import { useRef, useCallback, useEffect } from 'react'; +import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary.std.ts'; type CallbackType = (toFocus: HTMLElement | null | undefined) => void; @@ -9,11 +10,15 @@ type CallbackType = (toFocus: HTMLElement | null | undefined) => void; export const useRestoreFocus = (): [CallbackType] => { const toFocusRef = useRef(null); const lastFocusedRef = useRef(null); + const focusLastElementTimeoutRef = + useRef>(null); // We need to use a callback here because refs aren't necessarily populated on first // render. For example, ModalHost makes a top-level parent div first, and then renders // into it. And the children you pass it don't have access to that root div. const setFocusRef = useCallback((toFocus: HTMLElement | null | undefined) => { + clearTimeoutIfNecessary(focusLastElementTimeoutRef.current); + if (!toFocus) { return; } @@ -32,7 +37,7 @@ export const useRestoreFocus = (): [CallbackType] => { useEffect(() => { return () => { // On unmount, returned focus to element focused before we set the focus - setTimeout(() => { + focusLastElementTimeoutRef.current = setTimeout(() => { if (lastFocusedRef.current && lastFocusedRef.current.focus) { lastFocusedRef.current.focus(); }