Реализация поддержки VoiceOver/Screen Reader для сайта (WCAG)
Screen reader — программа экранного доступа для незрячих и слабовидящих пользователей. VoiceOver (macOS/iOS), NVDA и JAWS (Windows), TalkBack (Android) озвучивают содержимое страницы и дают возможность навигации с клавиатуры. WCAG 2.1 уровень AA обязывает обеспечить полную доступность для screen reader.
Как работает screen reader
Screen reader читает DOM-дерево и использует Accessibility Tree — специальное представление страницы, которое браузер строит из HTML и ARIA-атрибутов. Пользователь перемещается по заголовкам (H1-H6), ссылкам, формам, таблицам с помощью специальных клавиш.
Базовые требования
Семантическая разметка вместо div-soup:
<!-- Плохо -->
<div class="header">
<div class="nav">
<div class="nav-item" onclick="go('/home')">Главная</div>
</div>
</div>
<!-- Хорошо -->
<header>
<nav aria-label="Основная навигация">
<ul>
<li><a href="/home">Главная</a></li>
</ul>
</nav>
</header>
Ориентиры (landmarks) для навигации:
<header>...</header>
<nav aria-label="Основная навигация">...</nav>
<main>
<article>...</article>
<aside aria-label="Связанные материалы">...</aside>
</main>
<footer>...</footer>
Формы
<!-- Каждое поле формы должно иметь метку -->
<label for="email">Email</label>
<input type="email" id="email" name="email"
aria-describedby="email-hint email-error"
aria-required="true">
<span id="email-hint" class="hint">Мы не будем отправлять спам</span>
<span id="email-error" role="alert" aria-live="polite"></span>
<!-- Сгруппированные поля -->
<fieldset>
<legend>Способ оплаты</legend>
<label><input type="radio" name="payment" value="card"> Карта</label>
<label><input type="radio" name="payment" value="cash"> Наличные</label>
</fieldset>
Динамический контент и SPA
Основная проблема SPA для screen reader — динамические обновления DOM не объявляются автоматически.
// React — объявление о загрузке данных
function DataSection({ isLoading, data }) {
return (
<section>
{/* aria-live для объявления изменений */}
<div aria-live="polite" aria-atomic="true" className="sr-only">
{isLoading ? 'Загрузка данных...' : 'Данные загружены'}
</div>
{isLoading ? (
<div aria-busy="true">
<span className="sr-only">Загрузка...</span>
<Spinner aria-hidden="true" />
</div>
) : (
<ul>
{data.map(item => <li key={item.id}>{item.title}</li>)}
</ul>
)}
</section>
);
}
Управление фокусом при навигации
// При переходе между страницами в SPA — перенос фокуса на заголовок страницы
import { useEffect, useRef } from 'react';
import { useLocation } from 'react-router-dom';
function PageTitle({ title }) {
const titleRef = useRef(null);
const location = useLocation();
useEffect(() => {
titleRef.current?.focus();
document.title = title;
}, [location.pathname]);
return (
<h1 tabIndex={-1} ref={titleRef} className="focus-invisible">
{title}
</h1>
);
}
Изображения и медиа
<!-- Информативное изображение -->
<img src="graph.png" alt="График роста продаж: +35% в Q3 2024">
<!-- Декоративное изображение -->
<img src="decorative-bg.png" alt="" role="presentation">
<!-- Сложная диаграмма — дополнительное текстовое описание -->
<figure>
<img src="complex-chart.png" alt="Диаграмма распределения доходов"
aria-describedby="chart-desc">
<figcaption id="chart-desc">
Диаграмма показывает: 40% доходов — продукт A, 35% — продукт B, 25% — прочее.
</figcaption>
</figure>
Тестирование со screen reader
Инструменты:
- NVDA (Windows, бесплатно) — наиболее распространённый
- JAWS (Windows, коммерческий) — корпоративный стандарт
- VoiceOver (macOS: Cmd+F5, iOS: тройное нажатие Home)
- ChromeVox (Chrome extension)
Автоматическое тестирование:
- axe-core — библиотека для Playwright/Cypress
- jest-axe — для unit-тестов React компонентов
// Jest + jest-axe
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
test('form is accessible', async () => {
const { container } = render(<ContactForm />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
Срок реализации
- Аудит текущего состояния: 1–2 дня
- Исправление семантики и ARIA на существующем проекте: 3–7 дней
- Тестирование со screen reader + доводка: 2–3 дня







