ensure alt text is scrollable (#10671)

Co-authored-by: Eric Bailey <git@esb.lol>
This commit is contained in:
Spence Pope
2026-06-04 12:24:50 -04:00
committed by GitHub
parent d3c9ed027c
commit 58783ca5ae
2 changed files with 29 additions and 7 deletions
+18 -6
View File
@@ -1,5 +1,5 @@
import {useCallback, useEffect, useRef, useState} from 'react' import {useCallback, useEffect, useRef, useState} from 'react'
import {Pressable, StyleSheet, View} from 'react-native' import {Pressable, ScrollView, StyleSheet, View} from 'react-native'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {FocusGuards, FocusScope} from 'radix-ui/internal' import {FocusGuards, FocusScope} from 'radix-ui/internal'
@@ -226,17 +226,21 @@ function LightboxGallery({
)} )}
</View> </View>
{img.alt ? ( {img.alt ? (
<View <ScrollView
// Cap the overlay height so long alt text scrolls within the overlay
// instead of growing past the top of the screen and pushing the image
// out of view. Only scrollable once expanded.
style={[ style={[
a.px_4xl, styles.altScroll,
a.py_2xl,
{ {
backgroundColor: 'rgba(0, 0, 0, 0.5)', backgroundColor: 'rgba(0, 0, 0, 0.5)',
// @ts-expect-error web only // @ts-expect-error web only
backdropFilter: 'blur(16px)', backdropFilter: 'blur(16px)',
}, },
delayedFadeInAnim, delayedFadeInAnim,
]}> ]}
scrollEnabled={isAltExpanded}
contentContainerStyle={[a.px_4xl, a.py_2xl]}>
<Pressable <Pressable
accessibilityLabel={l`Expand alt text`} accessibilityLabel={l`Expand alt text`}
accessibilityHint={l`If alt text is long, toggles alt text expanded state`} accessibilityHint={l`If alt text is long, toggles alt text expanded state`}
@@ -250,7 +254,7 @@ function LightboxGallery({
{img.alt} {img.alt}
</Text> </Text>
</Pressable> </Pressable>
</View> </ScrollView>
) : null} ) : null}
{imgs.length > 1 && ( {imgs.length > 1 && (
<div aria-live="polite" aria-atomic="true" style={a.sr_only}> <div aria-live="polite" aria-atomic="true" style={a.sr_only}>
@@ -449,6 +453,14 @@ const styles = StyleSheet.create({
padding: 16, padding: 16,
boxSizing: 'border-box', boxSizing: 'border-box',
}, },
altScroll: {
// Size to content like the View it replaced, rather than filling the
// column via ScrollView's default flexGrow.
flexGrow: 0,
flexShrink: 0,
// @ts-ignore web-only -sfn
maxHeight: '50vh',
},
menuBtn: { menuBtn: {
top: 20, top: 20,
left: 20, left: 20,
+11 -1
View File
@@ -1,6 +1,9 @@
import {useRef} from 'react' import {useRef} from 'react'
import {LayoutAnimation, ScrollView, StyleSheet, View} from 'react-native' import {LayoutAnimation, ScrollView, StyleSheet, View} from 'react-native'
import {useSafeAreaInsets} from 'react-native-safe-area-context' import {
useSafeAreaFrame,
useSafeAreaInsets,
} from 'react-native-safe-area-context'
import {BlurView} from 'expo-blur' import {BlurView} from 'expo-blur'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
@@ -17,10 +20,16 @@ export function Footer({altText, isAltExpanded, onToggleAltExpanded}: Props) {
const {t: l} = useLingui() const {t: l} = useLingui()
const t = useTheme() const t = useTheme()
const insets = useSafeAreaInsets() const insets = useSafeAreaInsets()
const {height: screenHeight} = useSafeAreaFrame()
const isMomentumScrolling = useRef(false) const isMomentumScrolling = useRef(false)
if (!altText) return null if (!altText) return null
// Cap the overlay height so long alt text - or text enlarged by the OS via
// Dynamic Type / font scaling - scrolls within the overlay instead of growing
// past the top of the screen. Leaves the upper half clear for the header.
const maxHeight = screenHeight / 2
return ( return (
<View <View
style={[ style={[
@@ -46,6 +55,7 @@ export function Footer({altText, isAltExpanded, onToggleAltExpanded}: Props) {
}), }),
]}> ]}>
<ScrollView <ScrollView
style={{maxHeight}}
scrollEnabled={isAltExpanded} scrollEnabled={isAltExpanded}
onMomentumScrollBegin={() => { onMomentumScrollBegin={() => {
isMomentumScrolling.current = true isMomentumScrolling.current = true