Я развиваю Accessibility Auditor Skill — инструкции для ИИ-агента, который проверяет доступность интерфейсов по исходному коду и обследует сайты. В первых версиях агент мог объявить проблемой отсутствие атрибута в одном файле, хотя нужное поведение уже обеспечивала библиотека. Или предложить исправление, не проверив, где ещё используется общий компонент.
Я незрячий программист и пользуюсь скринридером. Мне хотелось применять этот инструмент для поиска препятствий в интерфейсах: неподписанных кнопок, недоступных с клавиатуры действий, ошибок формы, которые скринридер не сообщает. Но сначала пришлось разобраться с ошибками самого аудита.
От места использования — к реализации
Я добавил в скилл требование: прежде чем объявлять проблему, проверить реализацию компонента, используемую библиотеку и общие обёртки.
Например, в месте использования поля может не быть aria-describedby — атрибута, который связывает элемент с поясняющим текстом. Однако эту связь может создавать компонент формы. Тогда добавлять её повторно незачем. Если связь действительно отсутствует в общем компоненте, исправление потребуется там, с проверкой всех затронутых полей.
Поэтому агент должен проследить:
какой элемент получает фокус;
откуда берётся его доступное имя;
где создаются подпись, подсказка и сообщение об ошибке;
как передаются их идентификаторы;
что делает библиотека установленной версии;
какие атрибуты передаёт вызывающий код и сохраняет ли их компонент.
Перед изменением нужно также проверить, что найденный файл участвует в нужном сценарии. В репозитории могут остаться старые компоненты с похожими названиями. Исправление такого файла не повлияет на действующий интерфейс.
Дальше эти требования уточнялись на конкретных PR. Я просил агента разбирать причины ошибок и дополнять скилл так, чтобы правила подходили другим проектам.
Уже существующие объявления скринридера
В PR RTL №1603 дорабатывались сообщения авторизации и загрузки. При разборе пришлось учитывать уже заданную семантику и убирать дублирующие атрибуты.
Роли status и alert сами задают режим объявления изменений. Библиотека формы тоже может связывать ошибку с полем. Если агент этого не учёл, он может предложить лишние атрибуты или слишком настойчивую озвучку.
В скилле закрепили требования: учитывать существующие связи и роли, проверять повторные объявления и выбирать подходящий режим. Для обычной подсказки не нужно автоматически ставить assertive или role="alert", которые могут прерывать текущую речь.
Отдельный случай — индикатор загрузки рядом с текстом. В итоговом diff RTL индикатор скрыт от скринридера, а текст остаётся в области статуса:
<div *ngIf="!selNode.settings.themeColor" class="rtl-spinner" role="status"> <mat-spinner color="accent" aria-hidden="true" /> <h4>Loading RTL...</h4> </div>
В инструкции добавили правило: если сообщение уже есть в тексте, не давать декоративному индикатору такое же доступное имя.
PR закрыт без слияния. Этот фрагмент показывает предложенное изменение; фактическую озвучку нужно проверять в работающем приложении со скринридером.
Кто использует общий интерфейс
В Vets-Who-Code исправлялась связь текстового поля с сообщением обратной связи через feedbackId. Свойство добавили слишком широко: оно оказалось доступно компонентам, которые не умели его обрабатывать.
При доработке агент разделил общий интерфейс и свойства текстовых полей, добавил проверки пользовательского feedbackId для Input и Textarea.
После этого в скилле закрепили проверку потребителей общего интерфейса. При добавлении свойства агент должен выяснить, какие компоненты его поддерживают и не попадёт ли оно лишним атрибутом в HTML.
Для идентификаторов нужно проверить оба варианта: автоматически созданное значение и значение, переданное вызывающим кодом. Иначе стандартный пример может работать, а форма с собственным ID — потерять связь с сообщением.
Где исправлять связь поля с ошибкой
Для общих компонентов формы я сохранил в материалах скилла отдельный порядок проверки.
Сначала агент находит элемент управления и сообщение об ошибке. Затем проверяет идентификатор сообщения, состояние aria-invalid у поля и связь между ними. При исправлении нужно сохранить уже существующие подсказки в aria-describedby.
Ещё один вариант — пользовательский элемент, который передают в компонент через slot или render prop. Библиотека может правильно настраивать собственный input, но не передавать нужные атрибуты пользовательскому элементу. В таком случае значения нужно предоставить через публичный интерфейс компонента и проверить оба способа использования тестами.
Зависимость от внутренней разметки библиотеки
В suitenumerique/docs исправление выбора формата экспорта зависело от внутреннего элемента Cunningham Select. При ревью возник вопрос о проверке итоговых aria-hidden и tabindex.
Библиотека может поменять разметку, и внутренний селектор перестанет работать. В скилле закрепили предпочтение публичного API компонента. Если приходится обращаться к внутреннему элементу, нужен регрессионный тест, проверяющий получившиеся атрибуты.
В той итерации агент добавил такие утверждения в Playwright-тест. Полный e2e-прогон тогда не выполнялся.
Как замечания попадали в следующие аудиты
Я не хотел заново объяснять эти требования при работе с каждым проектом. Поэтому после ошибок просил обновлять инструкции: учитывать поведение библиотеки, проверять потребителей общих интерфейсов, сохранять пользовательские атрибуты, тестировать зависимости от внутренней разметки.
У меня нет замера доли ложных находок до и после изменений. Я могу показать сами правила и случаи, после которых они появились. Анализ исходников, автоматические тесты и проверка со скринридером дают разные подтверждения — в отчёте агент должен указывать, что именно выполнил.
Принятый PR: подписи папок в Nextcloud iOS
В Nextcloud iOS, PR №4111, агент исправил подписи папок в окне выбора места сохранения при отправке файла из другого приложения. У строк папок не было нужного доступного имени: пользователь VoiceOver попадал на элемент, но не получал названия папки.
Агент добавил имя из названия папки и дополнительных сведений, которые уже отображались в ячейке. Пустые значения исключил, а строке назначил признак кнопки. PR приняли 28 мая 2026 года.
В этом случае проверили исходный код. Сборку iOS и проверку с VoiceOver на устройстве не проводили: в окружении не было Xcode.
Практика на открытых проектах
Некоторое время агент по моему поручению сам искал репозитории с проблемами доступности и готовил PR. Мне хотелось приносить пользу открытым проектам и показывать применение скилла на реальных задачах.
Позже я предложил собирать кандидатов в течение недели, а затем выбирать проект с несколькими связанными проблемами: формами, фокусом, клавиатурной навигацией. Перед работой нужно было проверить, нет ли уже похожего исправления.
Эта практика давала материал для доработки инструкций. В самом скилле я сохранял требования к разбору компонентов и проверке выводов. Поиск репозиториев и расписание подготовки PR относились к отдельным заданиям агента.
Публичный репозиторий Accessibility Auditor Skill. Состав опубликованной версии может отличаться от локальных рабочих инструкций, описанных в статье.