Skip to content

Commit ac51096

Browse files
fix: fix drawer tab order on web for better accessibility (react-navigation#12616)
**Motivation** Currently, the Drawer component on web has an incorrect tab order when positioned on the left side of the screen and when the `drawerType` is `'permanent'` . This creates accessibility issues for keyboard users and those using assistive technologies, as the tab sequence doesn't follow a logical left-to-right order when the drawer is on the left side. **Problem** The current implementation uses `flex-direction: row-reverse` to position the drawer on the left side. While this achieves the desired visual layout, it creates an incorrect tab order because the DOM elements are rendered in a sequence that doesn't match the visual layout. This violates accessibility best practices and makes navigation difficult for keyboard users. **Solution** This PR fixes the tab order by: 1. Removing the use of `flex-direction: row-reverse` 2. Ensuring the DOM elements are written in the proper left-to-right sequence based on the drawer position 3. Maintaining the same visual layout while improving accessibility See [MDS Documentation on tabindex](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/tabindex) > Warning: You are recommended to only use 0 and -1 as tabindex values. **Avoid using tabindex values greater than 0 and CSS properties that can change the order of focusable HTML elements (Ordering flex items)**. Doing so makes it difficult for people who rely on using keyboard for navigation or assistive technology to navigate and operate page content. **Instead, write the document with the elements in a logical sequence.** **Test plan** 1. The change is reflected on `Drawer Layout` in the example app. 2. Change the drawer type to "permanent" and ensure that the drawer position is set to "left" 3. Using the `tab` key on the keyboard, tab through the elements. 4. See that the tab order is in sequence. `Shift + tab` to tab in reverse. 5. Change the drawer position to "right" 6. Again, tab through the elements, and see that the tab order is in sequence, matching the visual layout. **Screenshots** **Before (Incorrect tab order)** https://github.com/user-attachments/assets/fa92e69b-ebfc-49f9-88bb-0ff7788da9fe **After (Corrected tab order)** https://github.com/user-attachments/assets/735f63d2-818e-455b-94c3-de3c847f3a95 **Everything working as before** https://github.com/user-attachments/assets/f163f9c7-6ce4-4650-b8c5-2cb610aa084e --------- Co-authored-by: Satyajit Sahoo <[email protected]>
1 parent e95e8bb commit ac51096

File tree

2 files changed

+48
-49
lines changed

2 files changed

+48
-49
lines changed

example/src/Screens/DrawerView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Drawer } from 'react-native-drawer-layout';
77

88
import { DrawerProgress } from '../Shared/DrawerProgress';
99

10-
const DRAWER_TYPES = ['front', 'back', 'slide'] as const;
10+
const DRAWER_TYPES = ['front', 'back', 'slide', 'permanent'] as const;
1111

1212
export function DrawerView() {
1313
const { showActionSheetWithOptions } = useActionSheet();

packages/react-native-drawer-layout/src/views/Drawer.tsx

Lines changed: 47 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -102,64 +102,62 @@ export function Drawer({
102102
}
103103
: null;
104104

105-
return (
106-
<View style={[styles.container, style]}>
107-
<DrawerProgressContext.Provider value={progress}>
105+
const drawerElement = (
106+
<View
107+
key="drawer"
108+
ref={drawerRef}
109+
style={[
110+
styles.drawer,
111+
{
112+
width: drawerWidth,
113+
position: drawerType === 'permanent' ? 'relative' : 'absolute',
114+
zIndex: drawerType === 'back' ? -1 : 1,
115+
},
116+
drawerAnimatedStyle,
117+
drawerStyle,
118+
]}
119+
>
120+
{renderDrawerContent()}
121+
</View>
122+
);
123+
124+
const mainContent = (
125+
<View key="main" style={styles.main}>
126+
<View style={[styles.content, contentAnimatedStyle]}>
108127
<View
109-
style={[
110-
styles.main,
111-
{
112-
flexDirection:
113-
drawerType === 'permanent'
114-
? (isRight && direction === 'ltr') ||
115-
(!isRight && direction === 'rtl')
116-
? 'row'
117-
: 'row-reverse'
118-
: 'row',
119-
},
120-
]}
128+
aria-hidden={isOpen && drawerType !== 'permanent'}
129+
style={styles.content}
121130
>
122-
<View style={[styles.content, contentAnimatedStyle]}>
123-
<View
124-
aria-hidden={isOpen && drawerType !== 'permanent'}
125-
style={styles.content}
126-
>
127-
{children}
128-
</View>
129-
{drawerType !== 'permanent' ? (
130-
<Overlay
131-
open={open}
132-
progress={progress}
133-
onPress={() => onClose()}
134-
style={overlayStyle}
135-
accessibilityLabel={overlayAccessibilityLabel}
136-
/>
137-
) : null}
138-
</View>
139-
<View
140-
ref={drawerRef}
141-
style={[
142-
styles.drawer,
143-
{
144-
width: drawerWidth,
145-
position: drawerType === 'permanent' ? 'relative' : 'absolute',
146-
zIndex: drawerType === 'back' ? -1 : 0,
147-
},
148-
drawerAnimatedStyle,
149-
drawerStyle,
150-
]}
151-
>
152-
{renderDrawerContent()}
153-
</View>
131+
{children}
154132
</View>
155-
</DrawerProgressContext.Provider>
133+
{drawerType !== 'permanent' ? (
134+
<Overlay
135+
open={open}
136+
progress={progress}
137+
onPress={() => onClose()}
138+
style={overlayStyle}
139+
accessibilityLabel={overlayAccessibilityLabel}
140+
/>
141+
) : null}
142+
</View>
156143
</View>
157144
);
145+
146+
return (
147+
<DrawerProgressContext.Provider value={progress}>
148+
<View style={[styles.container, style]}>
149+
{!isRight && drawerElement}
150+
{mainContent}
151+
{isRight && drawerElement}
152+
</View>
153+
</DrawerProgressContext.Provider>
154+
);
158155
}
159156

160157
const styles = StyleSheet.create({
161158
container: {
162159
flex: 1,
160+
flexDirection: 'row',
163161
},
164162
drawer: {
165163
top: 0,
@@ -172,5 +170,6 @@ const styles = StyleSheet.create({
172170
},
173171
main: {
174172
flex: 1,
173+
flexDirection: 'row',
175174
},
176175
});

0 commit comments

Comments
 (0)