WebValid
WebValid Team

Слепой код: Топ 7 критических ошибок доступности, которые всегда допускают ИИ-ассистенты

AI Accessibility A11y React Next.js QA

Контекст стека: Примеры в этой статье применимы к проектам на React, Next.js (App Router) и Vite. Однако принципы доступности универсальны для любого фреймворка (Vue, Svelte, Angular).

Вы только что «вайб-коднули» (vibe-code) потрясающую новую лендинг-страницу за считанные секунды с помощью вашего любимого ИИ-ассистента. Cursor или Copilot сгенерировали весь интерфейс по одному промпту. На вашем мониторе всё выглядит отлично, анимации плавные, а деплой прошел без сучка и задоринки. Но под капотом вы могли только что заблокировать доступ к вашему сайту пользователям, использующим экранные дикторы (screen readers) или клавиатурную навигацию.

Когда ИИ-модели генерируют код, они отдают приоритет визуальной эстетике и немедленной функциональности. Им не хватает контекстуального понимания семантической структуры, что часто приводит к нарушению тестов доступности. Если вы не проверяете их вывод, вы выпускаете продукт, который невидим для скринридеров и невозможен для навигации с клавиатуры.

Вот топ 7 критических ошибок доступности, которые вносит ИИ-код, и способы их обнаружения перед следующим деплоем.

1. «Div Soup» (суп из дивов) и фантомные кнопки

🔴 Критично · Недоступно для скринридеров · WCAG 4.1.2 (Name, Role, Value)

Если вы попросите ИИ-ассистента «создать кастомный выпадающий список», его любимым решением будет построить массивную стопку элементов <div> и повесить на них событие onClick. Мы называем это «супом из дивов».

Подход ИИ (До):

// ❌ Сгенерированный ИИ код: Визуально работает, абсолютно недоступен
<div onClick={() => setIsOpen(true)}>Открыть меню</div>

Почему это сломано: Элемент <div> по своей природе не является интерактивным. Когда скринридер встречает его, он просто читает «Открыть меню», никак не сообщая, что это кликабельное действие. Более того, пользователь, использующий клавишу Tab, полностью пропустит этот элемент, что делает ваш сайт непригодным для всех, кто не может пользоваться мышью.

Исправление (После):

// ✅ Правильный код: Нативная кнопка обеспечивает встроенную доступность
<button onClick={() => setIsOpen(true)}>Открыть меню</button>

Нативные элементы, такие как <button> и <a>, поставляются со встроенным управлением состоянием фокуса, привязками клавиш (Enter и Space) и ролями доступности. Если вам всё же необходимо использовать кастомный <div>, вы должны вручную добавить role="button" и tabIndex={0}, но почти всегда лучше заставить ИИ использовать семантический HTML.

Возникли проблемы с поиском всех «дивов-изгоев» в ваших компонентах? Ознакомьтесь с нашим руководством по Markdown-Driven QA, чтобы узнать, как автоматизировать эту проверку.

2. Поле ввода без подписи (Unlabeled labels)

🟡 Высокий приоритет · Отказ от заполнения форм · WCAG 1.3.1 (Info and Relationships)

Формы — это сердце любого SaaS-продукта. При генерации формы регистрации ИИ обычно создает красивый макет, используя текст placeholder как единственное обозначение поля.

Подход ИИ (До):

// ❌ Сгенерированный ИИ код: Отсутствует программная связь
<div>
  <span>Email адрес</span>
  <input
    type="email"
    placeholder="Введите ваш email"
  />
</div>

Почему это сломано: Визуальной группировки недостаточно. Скринридерам нужна прямая программная связь между подписью (label) и полем ввода. Без неё пользователь переходит к полю ввода, и скринридер просто объявляет: «Редактировать текст, пусто». Он понятия не имеет, какие данные нужно вводить.

Исправление (После):

// ✅ Правильный код: Программная связь подписи
<div>
  <label htmlFor="email-input">Email адрес</label>
  <input
    id="email-input"
    type="email"
    placeholder="john@example.com"
  />
</div>

Всегда инструктируйте свой ИИ: «используй правильно связанные теги <label> со свойствами htmlFor для всех полей ввода».

3. Клавиатурная ловушка в модальном окне

🔴 Критично · Пользователь заперт · WCAG 2.1.2 (No Keyboard Trap)

Модальные окна и диалоги крайне сложно реализовать правильно. Когда ИИ генерирует модалку «с нуля», он почти никогда не реализует правильное управление фокусом.

Когда открывается модальное окно, фокус клавиатуры должен быть заперт внутри него, чтобы пользователь случайно не провалился в фоновый контент. Однако ИИ часто забывает предусмотреть выход. Если нет кнопки закрытия, привязанной к клавише Esc, пользователь клавиатуры официально оказывается «запертым» и вынужден перезагружать браузер, чтобы выйти.

Решение: Хватит позволять ИИ писать сложную логику модалок на чистом JS. Прикажите ему использовать нативный элемент HTML <dialog>, который современные браузеры поддерживают «из коробки». Он автоматически обрабатывает захват фокуса и клавишу Esc.

// ✅ Правильный код: Использование нативного элемента dialog
const dialogRef = useRef<HTMLDialogElement>(null);

return (
  <dialog ref={dialogRef}>
    <h2>Подпишитесь на новости</h2>
    {/* Контент */}
    <button onClick={() => dialogRef.current?.close()}>Закрыть</button>
  </dialog>
);

4. Невидимые индикаторы фокуса

🟡 Высокий приоритет · Путаница при навигации · WCAG 2.4.13 (Focus Appearance)

ИИ-ассистенты обожают генерировать ультра-минималистичные дизайны. Обычная «фишка» сгенерированного ИИ CSS — удаление стандартных рамок фокуса (focus rings), потому что они «выглядят некрасиво».

// ❌ Сгенерированный ИИ код: Удаляет рамки фокуса
<button style={{ outline: "none" }}>Отправить</button>

Почему это сломано: Если вы удалите outline, клавиатурные навигаторы не увидят, где именно на странице они находятся. Стандарты WCAG 2.2 требуют высококонтрастного индикатора фокуса (коэффициент контрастности не менее 3:1), который четко виден. Если ваш ИИ удаляет outline по умолчанию, скажите ему заменить его на стилизованный вариант с использованием псевдокласса :focus-visible.

Если сгенерированные стили вызывают проблемы с производительностью в дополнение к доступности, прочитает наш разбор Топ 5 ошибок ИИ в CSS.

5. Пустой или галлюцинированный Alt-текст

🟠 Средний приоритет · Нарушение контекста · WCAG 1.1.1 (Non-text Content)

ИИ не понимает бизнес-контекста вашего приложения. Когда он генерирует тег <img>, он либо оставляет текст alt пустым, либо дико галлюцинирует на основе имени переменной.

// ❌ Сгенерированный ИИ код: Бесполезный или отсутствующий контекст
<img src="/assets/hero-bg.jpg" alt="image" />
<img src="/icons/checkmark.svg" alt="Checkmark icon vector graphic" />

Почему это сломано: Если изображение чисто декоративное (например, фоновый паттерн), оно обязано иметь пустой атрибут alt (alt=""). Это говорит скринридеру, что его можно безопасно игнорировать. Если же оно несет информацию (например, график), ему нужно точное описание. ИИ не может сделать это различие за вас. Вы должны вручную проверять теги alt, чтобы они несли реальную пользу.

6. Незаконная вложенность DOM (Разрушитель дерева доступности)

🔴 Критично · Ошибки парсинга · Ошибка синтаксиса HTML5

Когда вы просите ИИ «сделать кнопку, которая ведет к оформлению заказа», он часто генерирует незаконно вложенный HTML — например, помещая тег <button> внутрь тега <a>.

Подход ИИ (До):

// ❌ Сгенерированный ИИ код: Ошибочная вложенность ломает парсеры
<a href="/checkout">
  <button onClick={trackEvent}>Купить сейчас</button>
</a>

Почему это сломано: Современные браузеры пытаются исправлять ошибки синтаксиса на лету, что полностью разрушает дерево доступности (Accessibility Tree). Когда скринридер доходит до этого блока, он путается во вложенных интерактивных элементах. Он либо объявит их дважды, либо полностью пропустит, либо зациклит фокус клавиатуры.

Исправление (После):

// ✅ Правильный код: Семантическая ссылка, стилизованная под кнопку
import Link from "next/link";

<Link
  href="/checkout"
  className="btn-primary"
  onClick={() => {
    trackEvent("checkout_clicked");
  }}
>
  Купить сейчас
</Link>;

Чтобы поймать такие ошибки на ранней стадии, используйте сканер синтаксиса HTML для статического анализа структуры DOM на наличие недопустимых правил вложенности.

7. Ядерная бомба «Aria-Hidden»

🔴 Критично · Невидимый контент · WCAG 1.3.1 (Info and Relationships)

Сканеры Axe-core часто ловят разрушительную ошибку «ИИ-самоуверенности»: чрезмерную коррекцию. Если вы попросите ИИ «исправить проблему со скринридером на фоновом элементе», он часто вешает aria-hidden="true" на родительский контейнер-обертку всего приложения.

Подход ИИ (До):

// ❌ Сгенерированный ИИ код: Заставляет замолчать всё приложение
<main aria-hidden="true">
  <div className="decorative-background" />
  <form>
    <label htmlFor="email">Email</label>
    <input
      id="email"
      type="email"
    />
    <button>Отправить</button>
  </form>
</main>

Почему это сломано: Когда aria-hidden="true" применяется к родителю, каждый дочерний элемент внутри него стирается из дерева доступности. Вся форма становится полностью невидимой для незрячих пользователей, даже если сами поля ввода подписаны идеально. Визуальный интерфейс выглядит нетронутым, что делает этот баг почти невозможным для обнаружения без аудита Axe-Core.

Исправление (После):

// ✅ Правильный код: Скрытие только декоративного элемента
<main>
  <div
    aria-hidden="true"
    className="decorative-background"
  />
  <form>
    <label htmlFor="email">Email</label>
    <input
      id="email"
      type="email"
    />
    <button>Отправить</button>
  </form>
</main>

Факт-чек: Кризис долга доступности

Чтобы понять, почему полагаться только на ИИ опасно, посмотрите на текущее состояние веба. В отчете WebAIM Million 2024 были проанализированы топ-миллион домашних страниц, и выяснилось, что на 95,9% из них есть обнаруживаемые ошибки WCAG 2.

Наиболее распространенные сбои в точности совпадают с тем, что ИИ склонен галлюцинировать:

  1. Низкоконтрастный текст (81%)
  2. Отсутствие альтернативного текста для изображений (54%)
  3. Отсутствие подписей к полям ввода форм (48%)

Когда вы «вайб-кодите» без аудита, вы не просто совершаете ошибку — вы напрямую увеличиваете огромный разрыв в доступности сервисов.

Возможности аудита WebValid

ИИ-модели невероятны в написании кода, но они ужасны в оценке финального отрендеренного DOM. Вы не можете просто вставить код в ChatGPT и спросить: «Это доступно?», потому что отдельный React-компонент не показывает, как он ведет себя в браузере.

WebValid устраняет этот пробел, сканируя полностью отрендеренный HTML и выступая в качестве технического переводчика для вашего ИИ-напарника.

Область проверкиВозможности WebValidОграничения
Семантика / ARIA✅ Проверяет отрендеренный HTML.Не может определить, соответствует ли alt-текст бизнес-контексту.
Фокусные рамки✅ Проверяет наличие CSS состояний фокуса.Не может определить, достаточно ли красив субъективный контраст.
Метки форм✅ Проверяет программные связи (htmlFor).Не может определить, имеет ли текст метки логический смысл.

WebValid использует передовое безголовое тестирование в браузере для имитации того, как настоящий скринридер парсит ваш DOM, находя ошибки, оставленные ИИ-ассистентом.

Ваш чек-лист проверки доступности

Перед развертыванием сгенерированного ИИ кода пройдитесь по этому списку:

  1. Тест клавишей Tab: Отключите мышь и попробуйте перемещаться по сайту, используя только Tab, Shift + Tab и Enter.
  2. Тест модалок: Откройте каждое модальное окно и нажмите Esc. Оно закрывается?
  3. Тест диктора: Включите VoiceOver (Mac) или NVDA (Windows) и закройте глаза. Можете ли вы заполнить основную форму?

Ваш ИИ-пилот может писать отличный код — он просто не знает, в какой момент он ошибся. Если вы дадите ему карту ошибок, он сможет исправить всё сам. Не гадайте, соответствует ли ваш сайт стандартам. Получите детерминированный аудит вашего DOM, превратите его в промпт для исправления и решите все проблемы за считанные минуты.

Начать аудит бесплатно

Эта статья была полезна?