diff options
author | Samuel Newman <mozzius@protonmail.com> | 2024-11-05 16:48:36 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-05 16:48:36 +0000 |
commit | b0c5a37daabc04570d33776de6c13be87a795491 (patch) | |
tree | 1b14b115368ff28b677ae2a9313b2255e2db0030 | |
parent | 97721163439765f6f33bf850c657c30f452e2c48 (diff) | |
download | voidsky-b0c5a37daabc04570d33776de6c13be87a795491.tar.zst |
Improve accessibility for navigation on web (#6120)
* improve accessibility for bottom bar tabs * improve a11y for left nav * group main content into <main> * use flex_1 rather than absoluteFill
-rw-r--r-- | src/view/shell/bottom-bar/BottomBar.tsx | 4 | ||||
-rw-r--r-- | src/view/shell/bottom-bar/BottomBarWeb.tsx | 106 | ||||
-rw-r--r-- | src/view/shell/createNativeStackNavigatorWithAuth.tsx | 15 | ||||
-rw-r--r-- | src/view/shell/desktop/LeftNav.tsx | 113 |
4 files changed, 169 insertions, 69 deletions
diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx index 028b194c3..855ba21b2 100644 --- a/src/view/shell/bottom-bar/BottomBar.tsx +++ b/src/view/shell/bottom-bar/BottomBar.tsx @@ -200,7 +200,7 @@ export function BottomBar({navigation}: BottomTabBarProps) { accessibilityLabel={_(msg`Chat`)} accessibilityHint={ numUnreadMessages.count > 0 - ? `${numUnreadMessages.numUnread} unread` + ? _(msg`${numUnreadMessages.numUnread} unread items`) : '' } /> @@ -227,7 +227,7 @@ export function BottomBar({navigation}: BottomTabBarProps) { accessibilityHint={ numUnreadNotifications === '' ? '' - : `${numUnreadNotifications} unread` + : _(msg`${numUnreadNotifications} unread items`) } /> <Btn diff --git a/src/view/shell/bottom-bar/BottomBarWeb.tsx b/src/view/shell/bottom-bar/BottomBarWeb.tsx index 35d385593..9b34159d7 100644 --- a/src/view/shell/bottom-bar/BottomBarWeb.tsx +++ b/src/view/shell/bottom-bar/BottomBarWeb.tsx @@ -1,18 +1,14 @@ import React from 'react' import {View} from 'react-native' import Animated from 'react-native-reanimated' -import {useSafeAreaInsets} from 'react-native-safe-area-context' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useNavigationState} from '@react-navigation/native' import {useMinimalShellFooterTransform} from '#/lib/hooks/useMinimalShellTransform' -import {usePalette} from '#/lib/hooks/usePalette' -import {clamp} from '#/lib/numbers' import {getCurrentRoute, isTab} from '#/lib/routes/helpers' import {makeProfileLink} from '#/lib/routes/links' import {CommonNavigatorParams} from '#/lib/routes/types' -import {s} from '#/lib/styles' import {useUnreadMessageCount} from '#/state/queries/messages/list-converations' import {useUnreadNotifications} from '#/state/queries/notifications/unread' import {useSession} from '#/state/session' @@ -23,6 +19,7 @@ import {Link} from '#/view/com/util/Link' import {Text} from '#/view/com/util/text/Text' import {Logo} from '#/view/icons/Logo' import {Logotype} from '#/view/icons/Logotype' +import {atoms as a, useTheme} from '#/alf' import { Bell_Filled_Corner0_Rounded as BellFilled, Bell_Stroke2_Corner0_Rounded as Bell, @@ -46,8 +43,7 @@ import {styles} from './BottomBarStyles' export function BottomBarWeb() { const {_} = useLingui() const {hasSession, currentAccount} = useSession() - const pal = usePalette('default') - const safeAreaInsets = useSafeAreaInsets() + const t = useTheme() const footerMinimalShellTransform = useMinimalShellFooterTransform() const {requestSwitchToAccount} = useLoggedOutViewControls() const closeAllActiveElements = useCloseAllActiveElements() @@ -69,12 +65,12 @@ export function BottomBarWeb() { return ( <Animated.View + role="navigation" style={[ styles.bottomBar, styles.bottomBarWeb, - pal.view, - pal.border, - {paddingBottom: clamp(safeAreaInsets.bottom, 15, 30)}, + t.atoms.bg, + t.atoms.border_contrast_low, footerMinimalShellTransform, ]}> {hasSession ? ( @@ -84,8 +80,9 @@ export function BottomBarWeb() { const Icon = isActive ? HomeFilled : Home return ( <Icon + aria-hidden={true} width={iconWidth + 1} - style={[styles.ctrlIcon, pal.text, styles.homeIcon]} + style={[styles.ctrlIcon, t.atoms.text, styles.homeIcon]} /> ) }} @@ -95,8 +92,9 @@ export function BottomBarWeb() { const Icon = isActive ? MagnifyingGlassFilled : MagnifyingGlass return ( <Icon + aria-hidden={true} width={iconWidth + 2} - style={[styles.ctrlIcon, pal.text, styles.searchIcon]} + style={[styles.ctrlIcon, t.atoms.text, styles.searchIcon]} /> ) }} @@ -104,43 +102,41 @@ export function BottomBarWeb() { {hasSession && ( <> - <NavItem routeName="Messages" href="/messages"> + <NavItem + routeName="Messages" + href="/messages" + badge={ + unreadMessageCount.count > 0 + ? unreadMessageCount.numUnread + : undefined + }> {({isActive}) => { const Icon = isActive ? MessageFilled : Message return ( - <> - <Icon - width={iconWidth - 1} - style={[styles.ctrlIcon, pal.text, styles.messagesIcon]} - /> - {unreadMessageCount.count > 0 && ( - <View style={styles.notificationCount}> - <Text style={styles.notificationCountLabel}> - {unreadMessageCount.numUnread} - </Text> - </View> - )} - </> + <Icon + aria-hidden={true} + width={iconWidth - 1} + style={[ + styles.ctrlIcon, + t.atoms.text, + styles.messagesIcon, + ]} + /> ) }} </NavItem> - <NavItem routeName="Notifications" href="/notifications"> + <NavItem + routeName="Notifications" + href="/notifications" + badge={notificationCountStr}> {({isActive}) => { const Icon = isActive ? BellFilled : Bell return ( - <> - <Icon - width={iconWidth} - style={[styles.ctrlIcon, pal.text, styles.bellIcon]} - /> - {notificationCountStr !== '' && ( - <View style={styles.notificationCount}> - <Text style={styles.notificationCountLabel}> - {notificationCountStr} - </Text> - </View> - )} - </> + <Icon + aria-hidden={true} + width={iconWidth} + style={[styles.ctrlIcon, t.atoms.text, styles.bellIcon]} + /> ) }} </NavItem> @@ -158,8 +154,13 @@ export function BottomBarWeb() { const Icon = isActive ? UserCircleFilled : UserCircle return ( <Icon + aria-hidden={true} width={iconWidth} - style={[styles.ctrlIcon, pal.text, styles.profileIcon]} + style={[ + styles.ctrlIcon, + t.atoms.text, + styles.profileIcon, + ]} /> ) }} @@ -184,7 +185,7 @@ export function BottomBarWeb() { <View style={{flexDirection: 'row', alignItems: 'center', gap: 12}}> <Logo width={32} /> <View style={{paddingTop: 4}}> - <Logotype width={80} fill={pal.text.color} /> + <Logotype width={80} fill={t.atoms.text.color} /> </View> </View> @@ -193,7 +194,7 @@ export function BottomBarWeb() { onPress={showCreateAccount} accessibilityHint={_(msg`Sign up`)} accessibilityLabel={_(msg`Sign up`)}> - <Text type="md" style={[{color: 'white'}, s.bold]}> + <Text type="md" style={[{color: 'white'}, a.font_bold]}> <Trans>Sign up</Trans> </Text> </Button> @@ -203,7 +204,7 @@ export function BottomBarWeb() { onPress={showSignIn} accessibilityHint={_(msg`Sign in`)} accessibilityLabel={_(msg`Sign in`)}> - <Text type="md" style={[pal.text, s.bold]}> + <Text type="md" style={[t.atoms.text, a.font_bold]}> <Trans>Sign in</Trans> </Text> </Button> @@ -219,7 +220,9 @@ const NavItem: React.FC<{ children: (props: {isActive: boolean}) => React.ReactChild href: string routeName: string -}> = ({children, href, routeName}) => { + badge?: string +}> = ({children, href, routeName, badge}) => { + const {_} = useLingui() const {currentAccount} = useSession() const currentRoute = useNavigationState(state => { if (!state) { @@ -235,8 +238,21 @@ const NavItem: React.FC<{ : isTab(currentRoute.name, routeName) return ( - <Link href={href} style={styles.ctrl} navigationAction="navigate"> + <Link + href={href} + style={[styles.ctrl, a.pb_lg]} + navigationAction="navigate" + aria-role="link" + aria-label={routeName} + accessible={true}> {children({isActive})} + {!!badge && ( + <View + style={styles.notificationCount} + aria-label={_(msg`${badge} unread items`)}> + <Text style={styles.notificationCountLabel}>{badge}</Text> + </View> + )} </Link> ) } diff --git a/src/view/shell/createNativeStackNavigatorWithAuth.tsx b/src/view/shell/createNativeStackNavigatorWithAuth.tsx index 7842fd5c8..9bcb91b7a 100644 --- a/src/view/shell/createNativeStackNavigatorWithAuth.tsx +++ b/src/view/shell/createNativeStackNavigatorWithAuth.tsx @@ -34,6 +34,7 @@ import {LoggedOut} from '#/view/com/auth/LoggedOut' import {Deactivated} from '#/screens/Deactivated' import {Onboarding} from '#/screens/Onboarding' import {SignupQueued} from '#/screens/SignupQueued' +import {atoms as a} from '#/alf' import {BottomBarWeb} from './bottom-bar/BottomBarWeb' import {DesktopLeftNav} from './desktop/LeftNav' import {DesktopRightNav} from './desktop/RightNav' @@ -137,12 +138,14 @@ function NativeStackNavigator({ return ( <NavigationContent> - <NativeStackView - {...rest} - state={state} - navigation={navigation} - descriptors={newDescriptors} - /> + <View role="main" style={a.flex_1}> + <NativeStackView + {...rest} + state={state} + navigation={navigation} + descriptors={newDescriptors} + /> + </View> {isWeb && showBottomBar && <BottomBarWeb />} {isWeb && !showBottomBar && ( <> diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx index de3a8190d..acbd5076a 100644 --- a/src/view/shell/desktop/LeftNav.tsx +++ b/src/view/shell/desktop/LeftNav.tsx @@ -151,6 +151,7 @@ interface NavItemProps { } function NavItem({count, href, icon, iconFilled, label}: NavItemProps) { const t = useTheme() + const {_} = useLingui() const {currentAccount} = useSession() const {gtMobile, gtTablet} = useBreakpoints() const isTablet = gtMobile && !gtTablet @@ -199,7 +200,7 @@ function NavItem({count, href, icon, iconFilled, label}: NavItemProps) { // @ts-ignore web only -prf href={href} dataSet={{noUnderline: 1}} - accessibilityRole="tab" + role="link" accessibilityLabel={label} accessibilityHint=""> <View @@ -219,6 +220,9 @@ function NavItem({count, href, icon, iconFilled, label}: NavItemProps) { {isCurrent ? iconFilled : icon} {typeof count === 'string' && count ? ( <Text + accessibilityLabel={_(msg`${count} unread items`)} + accessibilityHint="" + accessible={true} style={[ a.absolute, a.text_xs, @@ -307,7 +311,7 @@ function ComposeBtn() { <View style={[a.flex_row, a.pl_md, a.pt_xl]}> <Button disabled={isFetchingHandle} - label={_(msg`New post`)} + label={_(msg`Compose new post`)} onPress={onPressCompose} size="large" variant="solid" @@ -331,8 +335,16 @@ function ChatNavItem() { <NavItem href="/messages" count={numUnreadMessages.numUnread} - icon={<Message style={pal.text} width={NAV_ICON_WIDTH} />} - iconFilled={<MessageFilled style={pal.text} width={NAV_ICON_WIDTH} />} + icon={ + <Message style={pal.text} aria-hidden={true} width={NAV_ICON_WIDTH} /> + } + iconFilled={ + <MessageFilled + style={pal.text} + aria-hidden={true} + width={NAV_ICON_WIDTH} + /> + } label={_(msg`Chat`)} /> ) @@ -351,6 +363,7 @@ export function DesktopLeftNav() { return ( <View + role="navigation" style={[ styles.leftNav, isTablet && styles.leftNavTablet, @@ -371,23 +384,57 @@ export function DesktopLeftNav() { <NavItem href="/" - icon={<Home width={NAV_ICON_WIDTH} style={pal.text} />} - iconFilled={<HomeFilled width={NAV_ICON_WIDTH} style={pal.text} />} + icon={ + <Home + aria-hidden={true} + width={NAV_ICON_WIDTH} + style={pal.text} + /> + } + iconFilled={ + <HomeFilled + aria-hidden={true} + width={NAV_ICON_WIDTH} + style={pal.text} + /> + } label={_(msg`Home`)} /> <NavItem href="/search" - icon={<MagnifyingGlass style={pal.text} width={NAV_ICON_WIDTH} />} + icon={ + <MagnifyingGlass + style={pal.text} + aria-hidden={true} + width={NAV_ICON_WIDTH} + /> + } iconFilled={ - <MagnifyingGlassFilled style={pal.text} width={NAV_ICON_WIDTH} /> + <MagnifyingGlassFilled + style={pal.text} + aria-hidden={true} + width={NAV_ICON_WIDTH} + /> } label={_(msg`Search`)} /> <NavItem href="/notifications" count={numUnreadNotifications} - icon={<Bell width={NAV_ICON_WIDTH} style={pal.text} />} - iconFilled={<BellFilled width={NAV_ICON_WIDTH} style={pal.text} />} + icon={ + <Bell + aria-hidden={true} + width={NAV_ICON_WIDTH} + style={pal.text} + /> + } + iconFilled={ + <BellFilled + aria-hidden={true} + width={NAV_ICON_WIDTH} + style={pal.text} + /> + } label={_(msg`Notifications`)} /> <ChatNavItem /> @@ -396,12 +443,14 @@ export function DesktopLeftNav() { icon={ <Hashtag style={pal.text as FontAwesomeIconStyle} + aria-hidden={true} width={NAV_ICON_WIDTH} /> } iconFilled={ <HashtagFilled style={pal.text as FontAwesomeIconStyle} + aria-hidden={true} width={NAV_ICON_WIDTH} /> } @@ -409,23 +458,55 @@ export function DesktopLeftNav() { /> <NavItem href="/lists" - icon={<List style={pal.text} width={NAV_ICON_WIDTH} />} - iconFilled={<ListFilled style={pal.text} width={NAV_ICON_WIDTH} />} + icon={ + <List + style={pal.text} + aria-hidden={true} + width={NAV_ICON_WIDTH} + /> + } + iconFilled={ + <ListFilled + style={pal.text} + aria-hidden={true} + width={NAV_ICON_WIDTH} + /> + } label={_(msg`Lists`)} /> <NavItem href={currentAccount ? makeProfileLink(currentAccount) : '/'} - icon={<UserCircle width={NAV_ICON_WIDTH} style={pal.text} />} + icon={ + <UserCircle + aria-hidden={true} + width={NAV_ICON_WIDTH} + style={pal.text} + /> + } iconFilled={ - <UserCircleFilled width={NAV_ICON_WIDTH} style={pal.text} /> + <UserCircleFilled + aria-hidden={true} + width={NAV_ICON_WIDTH} + style={pal.text} + /> } label={_(msg`Profile`)} /> <NavItem href="/settings" - icon={<Settings width={NAV_ICON_WIDTH} style={pal.text} />} + icon={ + <Settings + aria-hidden={true} + width={NAV_ICON_WIDTH} + style={pal.text} + /> + } iconFilled={ - <SettingsFilled width={NAV_ICON_WIDTH} style={pal.text} /> + <SettingsFilled + aria-hidden={true} + width={NAV_ICON_WIDTH} + style={pal.text} + /> } label={_(msg`Settings`)} /> |