Урок 10

Доступность

Приложения должны быть доступны всем: слабовидящим, людям с ограниченными возможностями.

accessibilityLabel

Текстовое описание элемента для screen reader:

<Pressable
    onPress={onClose}
    accessibilityLabel="Закрыть"
    accessibilityRole="button"
>
    <Ionicons name="close" size={24} />
</Pressable>

Без label скринридер прочитает имя иконки, а не её значение.

accessibilityRole

Роль элемента:

accessibilityRole="button"
accessibilityRole="link"
accessibilityRole="header"
accessibilityRole="image"
accessibilityRole="text"
accessibilityRole="search"
accessibilityRole="checkbox"
accessibilityRole="radio"
accessibilityRole="switch"
accessibilityRole="tab"
accessibilityRole="menu"
accessibilityRole="alert"

accessibilityState

Состояние:

<Pressable
    accessibilityState={{
        disabled: isDisabled,
        selected: isSelected,
        checked: isChecked,
        busy: isLoading,
        expanded: isExpanded,
    }}
>

accessibilityHint

Подсказка о том, что произойдёт:

<Pressable
    accessibilityLabel="Удалить"
    accessibilityHint="Удаляет запись без возможности восстановления"
    onPress={onDelete}
>
    <Text>Удалить</Text>
</Pressable>

accessibilityLiveRegion

Уведомление об изменениях:

<Text accessibilityLiveRegion="polite">
    Сохранено
</Text>

<Text accessibilityLiveRegion="assertive">
    Ошибка!
</Text>

polite — после текущего чтения. assertive — немедленно.

accessible

Группировка элементов:

<View accessible accessibilityLabel="Карточка пользователя: Алиса, 25 лет">
    <Text>Алиса</Text>
    <Text>25 лет</Text>
</View>

Скринридер прочитает всё как один элемент.

Кнопки с иконками

<Pressable
    onPress={onLike}
    accessibilityRole="button"
    accessibilityLabel={isLiked ? 'Убрать лайк' : 'Поставить лайк'}
    accessibilityState={{ selected: isLiked }}
    hitSlop={8}
>
    <Ionicons name={isLiked ? 'heart' : 'heart-outline'} size={24} />
</Pressable>

Формы

<Text nativeID="email-label">Email</Text>
<TextInput
    accessibilityLabelledBy="email-label"
    placeholder="Введите email"
    keyboardType="email-address"
    accessibilityHint="Ваш email для входа"
/>

Минимальный размер области нажатия

44×44 px — Apple HIG, 48×48 dp — Material Design:

<Pressable
    style={{ width: 48, height: 48, justifyContent: 'center', alignItems: 'center' }}
    onPress={onClose}
>
    <Ionicons name="close" size={24} />
</Pressable>

Или hitSlop:

<Pressable hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }} onPress={onClose}>

Контраст

Текст должен быть читаемым:

  • AA: контраст 4.5:1 для обычного, 3:1 для крупного
  • AAA: 7:1 для обычного, 4.5:1 для крупного

Инструменты: WebAIM Contrast Checker, Stark.

Динамический размер шрифта

Пользователь может увеличить шрифт в настройках. React Native учитывает это автоматически.

Отключить:

<Text allowFontScaling={false}>Текст</Text>

Но лучше не отключать — доступность важнее.

Screen reader

  • iOS: VoiceOver
  • Android: TalkBack

Включить в настройках или через меню разработчика.

Тестирование

  1. Включи VoiceOver или TalkBack
  2. Пройди по приложению с закрытыми глазами
  3. Проверь, что все элементы озвучиваются
  4. Проверь порядок фокуса
  5. Проверь, что кнопки нажимаются

Итоги

  • accessibilityLabel для элементов без текста
  • accessibilityRole для типа
  • accessibilityState для состояния
  • accessibilityHint для подсказок
  • 44×44 минимум для нажатия
  • Тестируй со screen reader