facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
119.56k stars 24.37k forks source link

[a11y] Nested accessibility items are not respected on iOS #24515

Open sahrens opened 5 years ago

sahrens commented 5 years ago

React Native apps often have many UI components that are touchable but also contain nested elements that are also touchable. For example, an entire list row may be touchable as a primary navigation option, but it might also have an auxiliary button nested inside of it for taking a specific action, like delete or save.

Currently, if the touchable wrapper for the auxiliary button as well as the row container are properly tagged with accessibility props and accessibility labels it works fine on Android but on iOS, the nested 'save/delete' button is not selectable by VoiceOver. On Android, both will be accessible when accessibility features are enabled.

The current workaround in user-land is to make some janky hierachry where the aux button is a sibling of its visual container, which often breaks encapsulation and is generally a pain.

There are a couple ways we might be able to work around this at the framework level:

1) use UIAccessibilityContainer protocol
2) set the accessibilityElements property on the parent

Those approaches are detailed a bit more here:
https://stackoverflow.com/questions/38849389/voiceover-parent-and-child-views-as-accessibility-elements

Looks like flutter had this same issue and fixed it here with UIAccessibilityContainer: https://github.com/flutter/engine/pull/4110

Tracked internally with T34121499

react-native-bot commented 5 years ago

Thanks for submitting your issue. Can you take another look at your description and make sure the issue template has been filled in its entirety?

👉 Click here if you want to take another look at the Bug Report issue template.

jessebeach commented 5 years ago

@sahrens this is also true on Web for roles that flatten their children into text, known as childPresentational.

https://www.w3.org/TR/wai-aria-1.1/#childrenArePresentational

Only Android allows nesting of interactive accessibility nodes within interactive accessibility nodes. We should disallow this practice altogether at the React Native level in order to satisfy the constraints of all target platforms. React Native should throw a run-time error when this nesting is detected. That would solve a category of issues that we're constantly chasing down on iOS and Web.

sahrens commented 5 years ago

@jessebeach: generally we try not to limit experiences based on lowest common denominator (especially if there are workarounds like flutter uses), but I agree we need to address this in a way that developers aren’t easily shooting themselves in the foot with a11y. Accessory buttons in list items are a particularly common pattern - what’s the recommended way to make them accessible?

@necolas: how have you dealt with this on react-native-web?

necolas commented 5 years ago

I haven't yet. But I have been planning to add runtime errors when nesting elements with accessibilityRole of link or button to avoid people stumbling into these issues.

You can still have nested Touchables without problem as long as the ancestral ones aren't given link or button roles. That allows mouse, touch, and keyboard interactions to function as expected but requires a little care if the functionality of the outer Touchable isn't redundant. Something like touching a twitter post navigates to the permalink, and the timestamp is marked up as a link for screen readers to be able to navigate there too. If you don't have redundancy you need to fall back to Touchables as siblings and use tricks like absolute positioning (emulating the background layer) to place them visually over each other.

jessebeach commented 5 years ago

If you don't have redundancy you need to fall back to Touchables as siblings and use tricks like absolute positioning (emulating the background layer) to place them visually over each other.

Accessory buttons in list items are a particularly common pattern - what’s the recommended way to make them accessible?

@necolas provided the answer here. The trick is to think of the link item not as the container, but as a sibling interactive element. The accessory buttons are siblings to it. The list item and the accessories are contained in a common parent node. The visual presentation gives the appearance of the list item interactive as the parent container, but that's just presentational fluff.

That's one solution the developer can take. She might however decide to use aria-activedescendant on Web or Rotor secondary actions on iOS. This is an implementation detail and depends on the design. The takeaway is, there are ways to build around the no-nesting limitation in developer land.

sahrens commented 5 years ago

I think what I was hoping for was a way to do those positioning gymnastics somewhat automatically in the framework, maybe with some special components to make it more explicit. I know it’s possible on iOS since flutter did it - is it feasible on web too?

-Spencer


From: J. Renée Beach notifications@github.com Sent: Thursday, April 18, 2019 10:17 PM To: facebook/react-native Cc: Spencer Ahrens; Mention Subject: Re: [facebook/react-native] [a11y] Nested accessibility items are not respected on iOS (#24515)

If you don't have redundancy you need to fall back to Touchables as siblings and use tricks like absolute positioning (emulating the background layer) to place them visually over each other.

Accessory buttons in list items are a particularly common pattern - what’s the recommended way to make them accessible?

@necolashttps://github.com/necolas provided the answer here. The trick is to think of the link item not as the container, but as a sibling interactive element. The accessory buttons are siblings to it. The list item and the accessories are contained in a common parent node. The visual presentation gives the appearance of the list item interactive as the parent container, but that's just presentational fluff.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/facebook/react-native/issues/24515#issuecomment-484769830, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AALQTR76FKMXY3M2SKUBWL3PRFITBANCNFSM4HG6K2OQ.

necolas commented 5 years ago

Turns out React already prints warnings to the console if you nest links or buttons.

is it feasible on web too

Not sure but it might be quite complicated as you'd need to preserve all the other expectations around nesting elements even thought they're not actually nested in the DOM.

stale[bot] commented 5 years ago

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.

stale[bot] commented 5 years ago

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information.