Самая неприятная ошибка формы - не неправильный цвет рамки. Это ситуация, когда человек ввёл данные, нажал кнопку и не понял, что произошло. Доступность помогает сделать результат действия ясным для всех: пользователя клавиатуры, экранного диктора, человека с увеличенным масштабом и посетителя, которому просто трудно разобрать мелкую подпись.
Свяжите подпись, подсказку и ошибку
У каждого поля должен быть постоянный контекст: как оно называется, в каком формате ожидается значение и что исправить при ошибке. label с htmlFor связывает видимую подпись с input. aria-describedby перечисляет дополнительные пояснения. aria-invalid отмечает уже установленную ошибку. Эти атрибуты выполняют разные задачи; один не заменяет другой.
TSX// Компонент поля: состояние ошибки приходит из формы
import { useId } from 'react'
export function EmailField({ error }: { error?: string }) {
const id = useId()
const helpId = id + '-help'
const errorId = id + '-error'
return (
<div>
<label htmlFor={id}>Электронная почта</label>
<p id={helpId}>На этот адрес отправим ответ.</p>
<input id={id} name="email" type="email"
autoComplete="email" required
aria-invalid={error ? true : undefined}
aria-describedby={error ? helpId + ' ' + errorId : helpId} />
{error ? <p id={errorId}>{error}</p> : null}
</div>
)
}
useId нужен, чтобы несколько экземпляров поля не получили одинаковые id. Компонент не выполняет всю валидацию и не отправляет запрос: он показывает переданное состояние. Нативный type=email помогает с форматом и клавиатурой мобильного устройства, но не доказывает существование почтового адреса. Сервер по-прежнему должен проверить входные данные.
Ошибка отвечает на вопрос «что исправить?»
«Ошибка валидации» описывает работу программы, но не действие человека. «Введите почту в формате name@example.com» помогает исправить значение. «Поле обязательно» допустимо для простого пропуска, но при сложном формате лучше объяснить правило заранее. Сообщение не должно ссылаться только на цвет: «исправьте красные поля» потеряет смысл для части пользователей.
Не объявляйте пустую форму ошибочной сразу при открытии. До попытки отправки человек ещё не обязан был всё заполнить. После проверки ошибку нужно сохранить достаточно долго, чтобы её прочитать и исправить. Мгновенно исчезающее сообщение создаёт гонку уже между интерфейсом и пользователем, а не между запросами.
Управление фокусом после отправки
Если полей немного, после неуспешной проверки переведите фокус на первое неверное поле. Если форма длинная, полезна сводка ошибок с ссылками на поля. Выберите один понятный способ ориентирования и проверьте его с экранным диктором. Одновременное объявление каждой ошибки через alert и всей сводки может стать слишком шумным.
При сетевом сбое не очищайте введённые данные. Сообщите, что отправка не завершилась, и дайте повторить действие. При успешной отправке нужен видимый результат, а не только исчезновение кнопки. Пока запрос выполняется, предотвращайте повторную отправку и объясняйте состояние кнопки. Механика блокировки не должна запирать пользователя в форме после ошибки.
Практический сценарий проверки
- Откройте страницу и используйте только Tab, Shift+Tab, Enter и Space. Все действия доступны, порядок фокуса соответствует чтению.
- Отправьте пустую форму. Появились конкретные ошибки, фокус оказался в ожидаемом месте, поле и сообщение связаны.
- Введите неправильный адрес, затем исправьте его. Устаревшая ошибка не должна продолжать объявляться после новой успешной проверки.
- Увеличьте масштаб и размер текста. Подписи, сообщения и кнопка не перекрывают друг друга; при 375 px нет скролла всей страницы по горизонтали.
- Подмените ответ сервера ошибкой. Введённое сохранилось, повторная попытка доступна, сообщение не содержит внутренний stack trace.
- Проверьте экранным диктором название поля, подсказку, состояние ошибки и итог отправки. Автоматический сканер не заменяет этот шаг.
Какие тесты имеют смысл
Тест «input существует» почти ничего не доказывает. Проверьте сценарий: найденное по label поле получает фокус после ошибки, aria-describedby указывает на существующий узел, запрос не ушёл при неверных данных, а после сетевого отказа содержимое сохранилось. Автоматический a11y-аудит хорошо находит часть структурных нарушений и контраст, но оценку понятности текста оставляет человеку.
Доступность - не слой украшения после готовой формы. Она влияет на компоненты, состояние и обработку ошибок. В frontend-программе эти навыки естественно связаны с React и тестированием. Попробуйте добавить к своему проекту описанный сценарий и сравнить результат с чек-листом практикума.
Первичные источники
Связи и сообщения: W3C WAI - Validating Input. Значение атрибута: MDN aria-invalid. Пример поля нужно дополнить логикой отправки, серверной проверкой и состоянием формы.