[APP-724] Collection of accessibility fixes (#949)
* Fix: include alt text on the web lightbox image * a11y: Dont read the 'ALT' label * a11y: remove a wrapper behavior from posts This appears to have been introduced with the goal of creating meta actions on posts, but the behavior seems counter-productive. The accessibility inspector was unable to access individual items within the post and therefore most content was simply skipped. There may be a way to support the post actions without losing the ability to access the inner elements but I couldnt find it. -prf * a11y: apply alt tags to image wrappers so they get read * a11y: set Link accessibilityLabel to the title if none set * a11y: skip the SANDBOX watermark * a11y: improve post meta to not read UI and give a useful date * ally: improve post controls * a11y: add labels to lightbox images on mobile * fix types
This commit is contained in:
parent
0163ba0af8
commit
bc55241c9a
19 changed files with 80 additions and 148 deletions
|
@ -88,6 +88,10 @@ export const Link = observer(function Link({
|
|||
props.dataSet.noUnderline = 1
|
||||
}
|
||||
|
||||
if (title && !props.accessibilityLabel) {
|
||||
props.accessibilityLabel = title
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
testID={testID}
|
||||
|
@ -171,6 +175,7 @@ export const DesktopWebTextLink = observer(function DesktopWebTextLink({
|
|||
text,
|
||||
numberOfLines,
|
||||
lineHeight,
|
||||
...props
|
||||
}: {
|
||||
testID?: string
|
||||
type?: TypographyVariant
|
||||
|
@ -179,6 +184,9 @@ export const DesktopWebTextLink = observer(function DesktopWebTextLink({
|
|||
text: string | JSX.Element
|
||||
numberOfLines?: number
|
||||
lineHeight?: number
|
||||
accessible?: boolean
|
||||
accessibilityLabel?: string
|
||||
accessibilityHint?: string
|
||||
}) {
|
||||
if (isDesktopWeb) {
|
||||
return (
|
||||
|
@ -190,6 +198,7 @@ export const DesktopWebTextLink = observer(function DesktopWebTextLink({
|
|||
text={text}
|
||||
numberOfLines={numberOfLines}
|
||||
lineHeight={lineHeight}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -199,7 +208,8 @@ export const DesktopWebTextLink = observer(function DesktopWebTextLink({
|
|||
type={type}
|
||||
style={style}
|
||||
numberOfLines={numberOfLines}
|
||||
lineHeight={lineHeight}>
|
||||
lineHeight={lineHeight}
|
||||
{...props}>
|
||||
{text}
|
||||
</Text>
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||
import {StyleSheet, View} from 'react-native'
|
||||
import {Text} from './text/Text'
|
||||
import {DesktopWebTextLink} from './Link'
|
||||
import {ago} from 'lib/strings/time'
|
||||
import {ago, niceDate} from 'lib/strings/time'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useStores} from 'state/index'
|
||||
import {UserAvatar} from './UserAvatar'
|
||||
|
@ -57,7 +57,11 @@ export const PostMeta = observer(function (opts: PostMetaOpts) {
|
|||
text={sanitizeDisplayName(displayName)}
|
||||
href={`/profile/${opts.authorHandle}`}
|
||||
/>
|
||||
<Text type="md" style={pal.textLight} lineHeight={1.2}>
|
||||
<Text
|
||||
type="md"
|
||||
style={pal.textLight}
|
||||
lineHeight={1.2}
|
||||
accessible={false}>
|
||||
·
|
||||
</Text>
|
||||
<DesktopWebTextLink
|
||||
|
@ -65,6 +69,8 @@ export const PostMeta = observer(function (opts: PostMetaOpts) {
|
|||
style={[styles.metaItem, pal.textLight]}
|
||||
lineHeight={1.2}
|
||||
text={ago(opts.timestamp)}
|
||||
accessibilityLabel={niceDate(opts.timestamp)}
|
||||
accessibilityHint=""
|
||||
href={opts.postHref}
|
||||
/>
|
||||
</View>
|
||||
|
@ -122,7 +128,7 @@ export const PostMeta = observer(function (opts: PostMetaOpts) {
|
|||
href={`/profile/${opts.authorHandle}`}
|
||||
/>
|
||||
</View>
|
||||
<Text type="md" style={pal.textLight} lineHeight={1.2}>
|
||||
<Text type="md" style={pal.textLight} lineHeight={1.2} accessible={false}>
|
||||
·
|
||||
</Text>
|
||||
<DesktopWebTextLink
|
||||
|
@ -130,6 +136,8 @@ export const PostMeta = observer(function (opts: PostMetaOpts) {
|
|||
style={[styles.metaItem, pal.textLight]}
|
||||
lineHeight={1.2}
|
||||
text={ago(opts.timestamp)}
|
||||
accessibilityLabel={niceDate(opts.timestamp)}
|
||||
accessibilityHint=""
|
||||
href={opts.postHref}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
@ -10,7 +10,10 @@ export function PostSandboxWarning() {
|
|||
if (store.session.isSandbox) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text type="title-2xl" style={[pal.text, styles.text]}>
|
||||
<Text
|
||||
type="title-2xl"
|
||||
style={[pal.text, styles.text]}
|
||||
accessible={false}>
|
||||
SANDBOX
|
||||
</Text>
|
||||
</View>
|
||||
|
|
|
@ -50,6 +50,8 @@ interface DropdownButtonProps {
|
|||
openToRight?: boolean
|
||||
rightOffset?: number
|
||||
bottomOffset?: number
|
||||
accessibilityLabel?: string
|
||||
accessibilityHint?: string
|
||||
}
|
||||
|
||||
export function DropdownButton({
|
||||
|
@ -63,6 +65,7 @@ export function DropdownButton({
|
|||
openToRight = false,
|
||||
rightOffset = 0,
|
||||
bottomOffset = 0,
|
||||
accessibilityLabel,
|
||||
}: PropsWithChildren<DropdownButtonProps>) {
|
||||
const ref1 = useRef<TouchableOpacity>(null)
|
||||
const ref2 = useRef<View>(null)
|
||||
|
@ -128,8 +131,8 @@ export function DropdownButton({
|
|||
hitSlop={HITSLOP}
|
||||
ref={ref1}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={`Opens ${numItems} options`}
|
||||
accessibilityHint={`Opens ${numItems} options`}>
|
||||
accessibilityLabel={accessibilityLabel || `Opens ${numItems} options`}
|
||||
accessibilityHint="">
|
||||
{children}
|
||||
</TouchableOpacity>
|
||||
)
|
||||
|
@ -246,7 +249,9 @@ export function PostDropdownBtn({
|
|||
testID={testID}
|
||||
style={style}
|
||||
items={dropdownItems}
|
||||
menuWidth={isWeb ? 220 : 200}>
|
||||
menuWidth={isWeb ? 220 : 200}
|
||||
accessibilityLabel="Additional post actions"
|
||||
accessibilityHint="">
|
||||
{children}
|
||||
</DropdownButton>
|
||||
)
|
||||
|
@ -335,6 +340,7 @@ const DropdownItems = ({
|
|||
key={index}
|
||||
style={[styles.menuItem]}
|
||||
onPress={() => onPressItem(index)}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={item.label}
|
||||
accessibilityHint={`Option ${index + 1} of ${numItems}`}>
|
||||
{item.icon && (
|
||||
|
|
|
@ -64,15 +64,14 @@ export function AutoSizedImage({
|
|||
delayPressIn={DELAY_PRESS_IN}
|
||||
style={[styles.container, style]}
|
||||
accessible={true}
|
||||
accessibilityLabel="Share image"
|
||||
accessibilityHint="Opens ways of sharing image">
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={alt || 'Image'}
|
||||
accessibilityHint="Tap to view fully">
|
||||
<Image
|
||||
style={[styles.image, {aspectRatio}]}
|
||||
source={uri}
|
||||
accessible={true} // Must set for `accessibilityLabel` to work
|
||||
accessible={false} // Must set for `accessibilityLabel` to work
|
||||
accessibilityIgnoresInvertColors
|
||||
accessibilityLabel={alt}
|
||||
accessibilityHint=""
|
||||
/>
|
||||
{children}
|
||||
</TouchableOpacity>
|
||||
|
|
|
@ -34,7 +34,7 @@ export const GalleryItem: FC<GalleryItemProps> = ({
|
|||
onPressIn={onPressIn ? () => onPressIn(index) : undefined}
|
||||
onLongPress={onLongPress ? () => onLongPress(index) : undefined}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="View image"
|
||||
accessibilityLabel={image.alt || 'Image'}
|
||||
accessibilityHint="">
|
||||
<Image
|
||||
source={{uri: image.thumb}}
|
||||
|
@ -47,7 +47,9 @@ export const GalleryItem: FC<GalleryItemProps> = ({
|
|||
</TouchableOpacity>
|
||||
{image.alt === '' ? null : (
|
||||
<View style={styles.altContainer}>
|
||||
<Text style={styles.alt}>ALT</Text>
|
||||
<Text style={styles.alt} accessible={false}>
|
||||
ALT
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
|
|
@ -72,8 +72,7 @@ export function PostHider({
|
|||
style={style}
|
||||
href={href}
|
||||
noFeedback
|
||||
accessible={true}
|
||||
accessibilityRole="none"
|
||||
accessible={false}
|
||||
{...props}>
|
||||
{children}
|
||||
</Link>
|
||||
|
|
|
@ -19,6 +19,7 @@ import {Text} from '../text/Text'
|
|||
import {PostDropdownBtn} from '../forms/DropdownButton'
|
||||
import {HeartIcon, HeartIconSolid, CommentBottomArrow} from 'lib/icons'
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {pluralize} from 'lib/strings/helpers'
|
||||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {useStores} from 'state/index'
|
||||
import {RepostButton} from './RepostButton'
|
||||
|
@ -170,7 +171,9 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
|||
hitSlop={HITSLOP}
|
||||
onPress={opts.onPressReply}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Reply"
|
||||
accessibilityLabel={`Reply (${opts.replyCount} ${
|
||||
opts.replyCount === 1 ? 'reply' : 'replies'
|
||||
})`}
|
||||
accessibilityHint="reply composer">
|
||||
<CommentBottomArrow
|
||||
style={[defaultCtrlColor, opts.big ? s.mt2 : styles.mt1]}
|
||||
|
@ -190,7 +193,9 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
|||
hitSlop={HITSLOP}
|
||||
onPress={onPressToggleLikeWrapper}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={opts.isLiked ? 'Unlike' : 'Like'}
|
||||
accessibilityLabel={`${opts.isLiked ? 'Unlike' : 'Like'} (${
|
||||
opts.likeCount
|
||||
} ${pluralize(opts.likeCount || 0, 'like')})`}
|
||||
accessibilityHint="">
|
||||
{opts.isLiked ? (
|
||||
<HeartIconSolid
|
||||
|
|
|
@ -4,6 +4,7 @@ import {RepostIcon} from 'lib/icons'
|
|||
import {s, colors} from 'lib/styles'
|
||||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {Text} from '../text/Text'
|
||||
import {pluralize} from 'lib/strings/helpers'
|
||||
import {useStores} from 'state/index'
|
||||
|
||||
const HITSLOP = {top: 5, left: 5, bottom: 5, right: 5}
|
||||
|
@ -49,7 +50,9 @@ export const RepostButton = ({
|
|||
onPress={onPressToggleRepostWrapper}
|
||||
style={styles.control}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={isReposted ? 'Undo repost' : 'Repost'}
|
||||
accessibilityLabel={`${
|
||||
isReposted ? 'Undo repost' : 'Repost'
|
||||
} (${repostCount} ${pluralize(repostCount || 0, 'repost')})`}
|
||||
accessibilityHint="">
|
||||
<RepostIcon
|
||||
style={
|
||||
|
|
|
@ -129,7 +129,9 @@ export function PostEmbeds({
|
|||
style={styles.singleImage}>
|
||||
{alt === '' ? null : (
|
||||
<View style={styles.altContainer}>
|
||||
<Text style={styles.alt}>ALT</Text>
|
||||
<Text style={styles.alt} accessible={false}>
|
||||
ALT
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</AutoSizedImage>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue