Доступные формы: подписи, подсказки, ошибки и автозаполнение
Как сделать форму, которую можно заполнить со скринридером, с клавиатуры и без лишних усилий: label, autocomplete, fieldset, сообщения об ошибках

Часть 3 из 8 серии «Доступный веб на практике». Предыдущая часть: Семантический HTML и альтернативный текст: фундамент доступного сайта.
Форма — место, где пользователь ближе всего к цели, и место, где он чаще всего застревает: у половины популярных сайтов есть поля без подписей. Разбираем, как сделать формы, которые заполняются легко — со скринридером, с клавиатуры и с менеджером паролей.
Формы
У каждого поля — видимая подпись
<!-- ❌ Placeholder вместо подписи -->
<input type="email" placeholder="Email">
<!-- ✅ -->
<label for="email">Электронная почта</label>
<input type="email" id="email" name="email" autocomplete="email">
Почему placeholder не заменяет подпись:
- исчезает при вводе — человек забывает, что вводит, особенно при проблемах с памятью и вниманием;
- обычно слишком бледный и не проходит по контрасту;
- поддержка его как доступного имени неоднородна.
Связь for + id даёт сразу три вещи: скринридер читает подпись при фокусе на поле, голосовое управление находит поле по подписи, а клик по подписи ставит фокус в поле — это увеличивает зону нажатия, особенно для флажков.
Подсказки и формат
<label for="phone">Телефон</label>
<p id="phone-hint" class="hint">Например, +1 503 555 0100</p>
<input type="tel" id="phone" name="phone"
autocomplete="tel"
aria-describedby="phone-hint">
aria-describedby прочитает подсказку после имени поля. Подсказка видима всем, а не спрятана в тултипе.
Атрибут autocomplete
Атрибут autocomplete с правильными токенами (name, email, tel, street-address, postal-code, cc-number, one-time-code и др.) позволяет браузеру заполнить форму за человека. Для людей с моторными и когнитивными нарушениями это огромная разница, а WCAG выделяет это в отдельный критерий 1.3.5 Identify Input Purpose. Полный список токенов — в спецификации HTML и на MDN.
Группы полей
Радиокнопки и флажки, объединённые общим вопросом, группируются через <fieldset> и <legend>:
<fieldset>
<legend>Удобный способ связи</legend>
<input type="radio" id="contact-phone" name="contact" value="phone">
<label for="contact-phone">Телефон</label>
<input type="radio" id="contact-email" name="contact" value="email">
<label for="contact-email">Электронная почта</label>
</fieldset>
Без legend пользователь скринридера услышит «Телефон, переключатель» и не поймёт, о чём вопрос.
Обязательные поля
<label for="name">Имя <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" required autocomplete="name">
Атрибут required сообщает скринридеру «обязательное». Звёздочка — визуальная подсказка, скрытая от скринридера, чтобы он не читал «звёздочка». В начале формы поясните: «Поля со звёздочкой обязательны».
Ошибки
Хорошее сообщение об ошибке:
- видно — рядом с полем, не только красной рамкой;
- связано с полем программно;
- объясняет, как исправить, а не просто «Неверный формат»;
- объявляется при отправке, а фокус переходит к первой ошибке.
<label for="email">Электронная почта</label>
<input type="email" id="email" name="email"
autocomplete="email"
aria-invalid="true"
aria-describedby="email-error">
<p id="email-error" class="error">
<svg aria-hidden="true" focusable="false">...</svg>
Адрес должен содержать символ @, например name@example.com
</p>
// При отправке: проверяем, показываем ошибки и переводим фокус к первой
form.addEventListener('submit', (event) => {
const invalid = [...form.elements].filter((el) => el.willValidate && !el.checkValidity());
if (invalid.length === 0) return;
event.preventDefault();
invalid.forEach((field) => showError(field)); // ставит aria-invalid и текст ошибки
invalid[0].focus();
});
При длинной форме полезна сводка ошибок в начале со ссылками на поля — такой подход используют, например, в дизайн-системе GOV.UK (GOV.UK Error summary).
Обратите внимание на иконку: ошибка не должна передаваться только цветом (подробнее в Контраст, анимация и размер кнопок: визуальная доступность сайта).
Не ломайте вставку и менеджеры паролей
Критерий 3.3.8 из WCAG 2.2 прямо говорит: вход не должен требовать запоминания или перепечатывания. На практике:
<!-- ✅ Даём менеджеру паролей сделать свою работу -->
<input type="email" id="login" autocomplete="username">
<input type="password" id="password" autocomplete="current-password">
<!-- Для кода из SMS -->
<input type="text" inputmode="numeric" autocomplete="one-time-code">
И не запрещайте вставку (onpaste="return false") в поле пароля или кода — это ничего не защищает, но мешает всем.
Серия «Доступный веб на практике»
- Доступность сайта: для кого она на самом деле и с чего начать
- Семантический HTML и альтернативный текст: фундамент доступного сайта
- Доступные формы: подписи, подсказки, ошибки и автозаполнение — вы здесь
- Клавиатура и фокус: как сделать сайт, которым можно пользоваться без мыши
- Контраст, анимация и размер кнопок: визуальная доступность сайта
- ARIA и динамический контент: когда атрибуты помогают, а когда вредят
- Доступность в React: id, фокус при смене маршрута и объявления
- Как тестировать доступность: автоматика, ручная проверка и чеклист
Следующая часть: Клавиатура и фокус: как сделать сайт, которым можно пользоваться без мыши
Agama Labs — разработка сайтов и веб-приложений. Если хотите проверить доступность своего сайта, напишите нам: hello@agamalabs.com


