From 6be7751769262cc38b67fa70b3030c5537beac9a Mon Sep 17 00:00:00 2001 From: Spence Pope Date: Mon, 16 Feb 2026 08:39:53 -0500 Subject: [PATCH] have back button dismiss lightbox on web (#9874) --- src/alf/atoms.ts | 16 ++++++++ .../VideoEmbedInner/VideoEmbedInnerWeb.tsx | 14 +------ src/view/com/lightbox/Lightbox.web.tsx | 41 ++++++++++++++++++- 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/alf/atoms.ts b/src/alf/atoms.ts index e117b5effc..ffac94a5da 100644 --- a/src/alf/atoms.ts +++ b/src/alf/atoms.ts @@ -108,6 +108,22 @@ export const atoms = { animation: `zoomIn ${EXP_CURVE} 0.3s, fadeIn ${EXP_CURVE} 0.3s`, }), + /** + * Visually hidden but available to screen readers (web). + * Use for live regions or off-screen labels (e.g. "Image 1 of 3"). + */ + sr_only: web({ + position: 'absolute', + width: 1, + height: 1, + padding: 0, + margin: -1, + overflow: 'hidden', + clip: 'rect(0,0,0,0)', + whiteSpace: 'nowrap', + borderWidth: 0, + }), + /** * {@link Layout.SCROLLBAR_OFFSET} */ diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx index 5d14161fe1..37b141132d 100644 --- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx +++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx @@ -72,19 +72,7 @@ export function VideoEmbedInnerWeb({ loop={loop} /> {embed.alt && ( -
+
{embed.alt}
)} diff --git a/src/view/com/lightbox/Lightbox.web.tsx b/src/view/com/lightbox/Lightbox.web.tsx index 34b88e0aa6..400258e0ee 100644 --- a/src/view/com/lightbox/Lightbox.web.tsx +++ b/src/view/com/lightbox/Lightbox.web.tsx @@ -1,4 +1,4 @@ -import {useCallback, useEffect, useState} from 'react' +import {useCallback, useEffect, useRef, useState} from 'react' import {Pressable, StyleSheet, View} from 'react-native' import {Image} from 'expo-image' import {msg} from '@lingui/macro' @@ -69,7 +69,13 @@ function LightboxContainer({ -
{children}
+
+ {children} +
) @@ -125,6 +131,32 @@ function LightboxGallery({ return () => window.removeEventListener('keydown', onKeyDown) }, [onKeyDown]) + // Push a history entry so the browser back button closes the lightbox + // instead of navigating away from the page. + const closedByPopStateRef = useRef(false) + useEffect(() => { + history.pushState({lightbox: true}, '') + + const handlePopState = () => { + closedByPopStateRef.current = true + onClose() + } + window.addEventListener('popstate', handlePopState) + + return () => { + window.removeEventListener('popstate', handlePopState) + // Only pop our entry if it's still the current one. If navigation + // already pushed a new entry on top, leave the orphaned entry — + // it shares the same URL so traversing through it is harmless. + if ( + !closedByPopStateRef.current && + (history.state as {lightbox?: boolean})?.lightbox + ) { + history.back() + } + } + }, [onClose]) + const delayedFadeInAnim = !reduceMotionEnabled && [ a.fade_in, {animationDelay: '0.2s', animationFillMode: 'both'}, @@ -205,6 +237,11 @@ function LightboxGallery({ ) : null} + {imgs.length > 1 && ( +
+ {_(msg`Image ${index + 1} of ${imgs.length}`)} +
+ )}