react-native-bottom-sheet icon indicating copy to clipboard operation
react-native-bottom-sheet copied to clipboard

[Bug]: [v5] When Talkback is enabled and a modal is displayed, the items below the modal sheet are still accessible by tap and the sheet is not focused

Open hraschan opened this issue 11 months ago • 27 comments

Version

v5

Reanimated Version

v3

Gesture Handler Version

v2

Platforms

Android

What happened?

  1. When Talkback is enabled Modal is not focused when presented
  2. Elements below the Modal are accessible.
  3. When dismissing modal, elements are no longer accessible/focusable (no green marker around them and no announcements)

There was already an issue in v4 #1641 but this issue still persists.

In general the accessibility behavior is questionable. Why is the backdrop accessible? Why is each element accessible. Handle, Container, Background. Content elements are not accessible seperatly.

Reproduction steps

  1. When Talkback is enabled Modal is not focused when presented
  2. Elements below the Modal are accessible
  3. When dismissing modal, elements are no longer accessible/focusable (no green marker around them and no announcements)

Describe what you expected to happen:

  1. When Talkback is enabled, if user opens Modal, it should get focus automatically
  2. Elements below the Modal should not be accessible by default (some prop needed maybe)
  3. After dismissing Modal, elements that were below it should be accessible/focusable again

Reproduction sample

https://snack.expo.dev/@gorhom/bottom-sheet---issue-reproduction-template

Relevant log output

No response

hraschan avatar Jan 14 '25 11:01 hraschan

We're having the same issue.

janReitan avatar Jan 14 '25 15:01 janReitan

++ Would love a resolution for this as well

laijoann avatar Jan 24 '25 21:01 laijoann

Made a patch-package to fix this issue if anyone's interested in a quick fix:

  • Added importantForAccessibility prop to BottomSheet, BottomSheetModal, BottomSheetBackdrop, BottomSheetBackground and BottomSheetContainer
  • Added logic to the BottomSheetModalProvider to disable accessibility for app child elements while giving accessibility to the modal content.

Based on the suggestions in https://github.com/gorhom/react-native-bottom-sheet/issues/687 Manually focusing the modal does not seem to be necessary once the rest of the content is non-accessible, as the screen reader will jump to the first available element.

@gorhom+bottom-sheet+5.0.6.patch Gist

I would suggest creating a custom handle for closing the modal that you can make accessible while hiding the backdrop like this:

Image

marcogravbrot avatar Feb 06 '25 01:02 marcogravbrot

@marcogravbrot thank you for the patch, works well! 😊

ochodnicky avatar Feb 06 '25 14:02 ochodnicky

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar Mar 09 '25 09:03 github-actions[bot]

+1 for this, please re-open, this is likely failing accessibility standards

DylanWard14 avatar Mar 10 '25 04:03 DylanWard14

Does not meet accessibility requirements. Please fix asap.

MisterMaroki avatar Mar 25 '25 10:03 MisterMaroki

Made a patch-package to fix this issue if anyone's interested in a quick fix:

  • Added importantForAccessibility prop to BottomSheet, BottomSheetModal, BottomSheetBackdrop, BottomSheetBackground and BottomSheetContainer
  • Added logic to the BottomSheetModalProvider to disable accessibility for app child elements while giving accessibility to the modal content.

Based on the suggestions in #687 Manually focusing the modal does not seem to be necessary once the rest of the content is non-accessible, as the screen reader will jump to the first available element.

@gorhom+bottom-sheet+5.0.6.patch Gist

I would suggest creating a custom handle for closing the modal that you can make accessible while hiding the backdrop like this:

Image

Ty mate! I also added accessibilityElementsHidden to the portal children wrapper to achieve same behaviour on iOS:

<PortalProvider>
            <View style={{flex: 1}} importantForAccessibility={isModalVisible ? "no-hide-descendants" : "yes"} accessibilityElementsHidden={!!isModalVisible} >
                {children}
            </View>
</PortalProvider>

This way i fixed the issue on iOS when the bottomsheetbackdrop and bottomsheetbackground had assessible false applied.

tastydev avatar Mar 28 '25 09:03 tastydev

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar Apr 28 '25 10:04 github-actions[bot]

Bump, still an issue

DylanWard14 avatar Apr 28 '25 10:04 DylanWard14

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar May 30 '25 09:05 github-actions[bot]

Stop using stale bot, this issue does not solve itself if it's ignored. Bump.

Brawl345 avatar Jun 03 '25 05:06 Brawl345

const setScreenReaderFocus = useCallback(() => {
      if (isWebDesktop || !pressableRef?.current) return;

      try {
        const pressableElement = pressableRef?.current;

        // Get the native node handle of the element
        const nodeHandle = findNodeHandle(pressableElement);

        if (nodeHandle) {
          // This is the most direct way to focus screen readers on an element
          AccessibilityInfo.setAccessibilityFocus(nodeHandle);
        } else {
        }
      } catch (error) {
        console.log('Error setting screen reader focus:', error);
      }
    }, [isWebDesktop, pressableRef]);



return (
      <BottomSheetModal
        {...props}
        aria-modal={true}
        ref={ref}
        accessible={false}
        onDismiss={handleOnDismiss}
        stackBehavior="push"
        onChange={index => {
          // Only set screen reader focus when the sheet is fully open (index 0 or greater)
          if (index >= 0) {
            setTimeout(() => {
              setScreenReaderFocus();
            }, 100);
          }
        }}
        keyboardBehavior={keyboardBehavior}
        bottomInset={bottomInset}
        style={[
          style.container,
          !hasMainPaddingNative ? {paddingHorizontal: 0} : null,
        ]}
        handleStyle={style.indicatorContainerStyle}
        backgroundStyle={[
          {
            backgroundColor: theme.colors.BOTTOMSHEET_BACKGROUND,
          },
          backgroundStyle,
        ]}
        enableContentPanningGesture={
          !isWebDesktop && isWeb ? false : enableContentPanningGesture
        }
        enablePanDownToClose={enablePanDownToClose}
        handleComponent={disableHandleIndicator ? null : BottomSheetHandle}
        handleIndicatorStyle={style.handler}
        snapPoints={snapPoints}
        backdropComponent={renderBackdrop}
        android_keyboardInputMode={android_keyboardInputMode}
        keyboardBlurBehavior={keyboardBlurBehavior}
        enableDynamicSizing={enableDynamicSizing}>
        <Pressable
          ref={pressableRef}
          accessible={false}
          accessibilityLabel={accessibilityLabel || "Bottom Sheet"}
          testID={`${testID}_pressable`}>
          <BottomSheetScrollView
            scrollEnabled={false}
            showsVerticalScrollIndicator={false}>
            <BottomSheetView
              style={!detached && {paddingBottom: detachedPaddingBottom}}
              accessibilityViewIsModal={true}
              importantForAccessibility={'yes'}
              testID={testID}>
              
              {children}
            </BottomSheetView>
          </BottomSheetScrollView>
        </Pressable>
      </BottomSheetModal>
    );
  },
);

1.Set accessible={false} on the BottomSheetModal component. 2.Wrap the content (e.g., BottomSheetView and its children) inside a Pressable component with a ref and accessible={false}. 3.Use findNodeHandle from React Native to get the native node handle of the Pressable. This creates a targetable element for Android’s TalkBack to focus on. Set accessible={false} on the Pressable to avoid it being a focus point itself, letting the inner content take focus.

christi10 avatar Jun 03 '25 11:06 christi10

any updates

anoopmm avatar Jun 08 '25 15:06 anoopmm

Confirmed that this is still an issue with RN 0.79.3 and bottom sheet version 5.1.4. @marcogravbrot I tried your solution and only ran into one issue; it did fix the problem on Android, but then on iOS, my backdrop could be "clicked through" which was the original problem on Android. So the way I fixed this to get it to work on both platforms was only to change the accessible prop in BottomSheetBackdrop to this:

accessible={Platform.OS === 'ios'}

The the final code in your project would be:

<BottomSheetModal
  accessible={false}
  importantForAccessibility="no"
  ref={bottomSheetModalRef}
  enableDynamicSizing
  enablePanDownToClose
  backdropComponent={props => (
    <BottomSheetBackdrop
      accessible={Platform.OS === 'ios'} // Without this, content behind backdrop can be pressed on iOS (Android is okay)
      importantForAccessibility="no"
      disappearsOnIndex={-1}
      appearsOnIndex={0}
      {...props}
    />
  )}
>

trooperandz avatar Jun 27 '25 20:06 trooperandz

@gorhom any way to prioritize fixing accessibility ? This is a significant demand in project in 2025.

I have added a console.log here to see which props are passed to BottomSheet component. I got a first render with my custom props and just after a 2nd render with undefined props. So I loose my accessibility props... Image (maybe related to how Portal works/teleport components ?)

There is also hard coded accessibility label: https://github.com/gorhom/react-native-bottom-sheet/blob/fc3409db0c58590f0bd71b2e5b45a4343242ce66/src/components/bottomSheetBackground/BottomSheetBackground.tsx#L14

Aure77 avatar Jun 30 '25 14:06 Aure77

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar Jul 31 '25 09:07 github-actions[bot]

not stale

Brawl345 avatar Jul 31 '25 10:07 Brawl345

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar Sep 01 '25 09:09 github-actions[bot]

not stale

Brawl345 avatar Sep 01 '25 09:09 Brawl345

@gorhom my team is also facing this same issue, and it's leaving us susceptible to legal action. Is there any chance we could prioritize an accessibility fix? These issues make your modal difficult to use by anyone with a screen reader, switch device, or bluetooth keyboard.

Watso196 avatar Sep 22 '25 18:09 Watso196

guys personaly i used react native modal with some adjustments to look like bottomsheet and works fine , with as much accessibility you want.

christi10 avatar Sep 22 '25 19:09 christi10

I'm still having this issue @5.2.6.

saltomorales avatar Sep 24 '25 12:09 saltomorales

I'm still having this issue @5.2.6.

if you want to see if it fits to your project , i am trying a little bit different appoach with react native's modal component . https://www.npmjs.com/package/rn-modal-bottom-sheet?activeTab=versions

christi10 avatar Oct 15 '25 10:10 christi10

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar Nov 20 '25 09:11 github-actions[bot]

not stale

saltomorales avatar Nov 20 '25 12:11 saltomorales

@gorhom is there any chance to resolve this issue?

dulyts avatar Nov 20 '25 13:11 dulyts