Обновлено: .

Нативный select — разумная отправная точка для обычного выбора. Кастомный компонент оправдан задачей продукта, но вместе с внешним видом вы берёте на себя его семантику, клавиатурное управление, фокус, состояния и совместимость со вспомогательными технологиями.

Начните с поля, которое уже умеет быть полем

<label for="topic">Тема обращения (обязательно)</label>
<select id="topic" name="topic" required>
  <option value="">Выберите тему</option>
  <option value="design">Дизайн интерфейса</option>
  <option value="development">Разработка</option>
</select>

Это законченная основа выбора, а не визуальная заглушка, которую нужно спрятать от скринридера. Ей всё ещё нужны хорошая компоновка, контраст, понятные варианты и обработка ошибок, но базовую модель взаимодействия предоставляет платформа.

Почему переключение между двумя select ненадёжно

Иногда предлагают показывать нативный select при :focus-visible, оставляя кастомный для клика. Такой подход создаёт две версии одного поля и не гарантирует доступность ни одной из них. Способ ввода не определяет наличие вспомогательной технологии: скринридер на телефоне работает с жестами, а человек может чередовать мышь и клавиатуру.

opacity: 0 не удаляет элемент из дерева доступности или порядка фокуса. pointer-events: none меняет обработку указателя, но не решает остальные вопросы. aria-hidden скрывает семантику, однако сам по себе не предотвращает фокус. В итоге можно получить невидимый фокусируемый элемент или два представления одного поля с разными значениями.

Гибрид возможен только как отдельно спроектированная и протестированная архитектура. Нужны единый источник значения, корректные reset, disabled, required, валидация и отправка формы. Само наличие скрытого нативного поля не доказывает доступность кастомной части.

Что входит в контракт кастомного выбора

Прежде чем писать обработчики, выберите точный паттерн. Выбор из списка без ввода и combobox с поиском — не одно и то же. Не смешивайте роли menu, listbox и combobox только потому, что все они визуально раскрываются вниз.

  • Доступное имя, роль и текущее значение должны быть понятны.
  • Раскрытое состояние и связь со списком должны отражаться программно.
  • Выбранный вариант и активный вариант — разные состояния; их нельзя путать.
  • Клавиши открытия, перемещения, подтверждения, отмены и поиска должны соответствовать выбранному паттерну.
  • Фокус либо перемещается по согласованной модели, либо остаётся на контроле с корректным aria-activedescendant.
  • Отправка, сброс, ошибки и недоступные варианты не должны расходиться с видимым интерфейсом.

Используйте паттерн combobox в APG как спецификацию поведения. Это не обещание, что любой скопированный пример будет одинаково работать со всеми браузерами и скринридерами: APG отдельно требует проверки совместимости.

Ошибка должна объяснять путь к исправлению

«Неверное значение» заставляет угадывать. «Укажите email в формате [email protected]» даёт действие. Не заменяйте label плейсхолдером: он исчезает при вводе. Сохраняйте уже введённые данные после неудачной отправки и связывайте пояснение с конкретным полем.

<!-- Пример состояния после обнаруженной ошибки -->
<label for="email">Электронная почта</label>
<input id="email" name="email" type="email" value="name@"
  autocomplete="email" aria-invalid="true"
  aria-describedby="email-error">
<p id="email-error">Укажите email в формате [email protected].</p>

aria-invalid не нужно выставлять заранее всем пустым полям. После исправления ошибки убирайте ошибочное состояние и обновляйте сообщение. При нескольких ошибках полезен общий список со ссылками на поля; при успешной отправке — понятное подтверждение, доступное без визуального поиска.

Модальное окно управляет не только картинкой

При открытии диалога нужен осмысленный начальный фокус. В модальном состоянии Tab не должен уходить в фон, фон не должен оставаться интерактивным. Должны быть понятное имя и доступный способ закрыть окно; после закрытия фокус обычно возвращается к инициатору или следующему логичному месту, если инициатор исчез.

Не назначайте aria-modal="true" окну, которое лишь выглядит модальным. Атрибут не реализует удержание фокуса и блокировку фона. У длинного диалога не всегда разумно ставить фокус сразу на первое поле: человек может пропустить заголовок и инструкцию.

Авторизация тоже часть доступности

Не блокируйте вставку пароля и работу менеджеров паролей. Для одноразовых кодов проверяйте вставку целиком и автозаполнение: декоративные шесть ячеек не должны требовать шести отдельных усилий. Критерий 3.3.8 AA ограничивает обязательные когнитивные тесты при входе, но содержит условия и исключения — это не абсолютный запрет всех CAPTCHA или паролей.

Проверяйте переходы между состояниями

Для одного поля нужны минимум состояния «пусто», «введено», «ошибка», «исправлено», «отправляется» и «готово». Для списка добавляются «открыт», «нет результатов» и «выбран вариант». В каждом проверяйте: что видно, что объявлено, где фокус, какое значение отправится, как отменить действие.

Такая таблица состояний обнаруживает больше реальных дефектов, чем сравнение закрытого select с макетом. Особенно внимательно проверяйте задержку сети: исчезающая кнопка отправки может унести фокус, а повторная активация — создать дубликат запроса. Решение должно сохранять понятный статус и предсказуемое управление.

Источники и статус стандартов проверены 5 ноября 2026 года.

Применить правила с ИИ-агентом

По результатам исследования мы создали открытые скиллы для Codex и Claude Code, которые помогают анализировать и улучшать сайты и приложения. Accessible UI направляет агента при проверке семантики, клавиатурного управления, фокуса, форм и контраста; Adaptive Interfaces помогает адаптировать интерфейс к размерам окна, масштабу текста, клавиатуре и складным устройствам. Агент изучает текущий проект, предлагает обоснованные улучшения и проверяет согласованные изменения. Скиллы не заменяют проверку с пользователями и вспомогательными технологиями.

Практические правила этой серии собраны в скилле Accessible UI на GitHub: семантика, клавиатура, фокус, формы, контраст и проверка результата. Инструкции написаны на английском. Порядок установки и примеры вызова приведены в начале серии.