Аудит доступності веб-сайту
Аудит доступності виявляє перешкоди, які заважають користувачам із порушеннями зору, слуху, моторики чи когнітивними особливостями ефективно працювати з вашим сайтом. Стандарт — WCAG 2.1/2.2 рівня AA, який є обов'язковим для державних веб-сайтів у багатьох країнах і фактичним стандартом для комерційних сайтів.
Що включає аудит
Автоматизоване сканування — виявляє приблизно 30–40% нарушень: відсутні alt-тексти, недостатній контраст, неправильна структура заголовків, відсутні ARIA-атрибути.
Ручне тестування — необхідне для інтерактивних елементів: навігація клавіатурою, сумісність зі скринридерами, поведінка модальних вікон та форм.
Тестування з реальними AT — NVDA + Firefox (Windows), VoiceOver + Safari (macOS/iOS), TalkBack (Android).
Автоматизоване сканування
# axe-core через CLI
npm install -g @axe-core/cli
axe https://mysite.com --reporter=json > axe-report.json
# Lighthouse Accessibility
npx lighthouse https://mysite.com \
--only-categories=accessibility \
--output=html \
--output-path=./lighthouse-a11y.html
# Pa11y: групове сканування всіх сторінок
npm install -g pa11y-ci
# .pa11yci
defaults:
standard: WCAG2AA
runners:
- axe
- htmlcs
timeout: 30000
urls:
- https://mysite.com
- https://mysite.com/about
- https://mysite.com/contact
- https://mysite.com/blog
pa11y-ci --config .pa11yci --json > pa11y-report.json
# Аналіз результатів
node -e "
const r = require('./pa11y-report.json');
const issues = Object.values(r.results).flat();
const errors = issues.filter(i => i.type === 'error');
console.log('Total errors:', errors.length);
errors.slice(0,10).forEach(e => console.log(e.code, e.selector));
"
Ключові критерії WCAG AA
| Критерій | Вимога | Типова помилка |
|---|---|---|
| 1.1.1 Alt Text | Усі зображення мають alt-текст | Декоративні зображення без alt="" |
| 1.3.1 Info and Relationships | Структура передана семантикою | <div> замість <button>, <nav>, <main> |
| 1.4.3 Contrast | 4.5:1 для тексту, 3:1 для великого тексту | Сірий текст на білому тлі |
| 1.4.4 Resize Text | До 200% без втрати контенту | Фіксована height у блоках |
| 2.1.1 Keyboard | Усе доступне через клавіатуру | Немає фокусу на користувацьких dropdown |
| 2.4.3 Focus Order | Логічний порядок фокусу | tabindex з довільними числами |
| 2.4.7 Focus Visible | Видимий індикатор фокусу | outline: none без альтернативи |
| 3.3.1 Error Identification | Помилки форм описані текстом | Тільки колір показує помилку |
| 4.1.2 Name, Role, Value | ARIA для користувацьких компонентів | Немає role/aria-label на кнопках-іконках |
Ручна перевірка: контрольний список
Навігація клавіатурою:
- Tab переміщається через усі інтерактивні елементи
- Shift+Tab працює у зворотному напрямку
- Enter/Space активує кнопки
- Escape закриває модальні вікна та dropdown
- Стрілки працюють усередину
<select>, radio groups, tablist
Пастка фокусу:
// Правильне управління фокусом у модальному вікні
const Modal = ({ isOpen, onClose, children }) => {
const modalRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isOpen) {
// Зберігаємо попередньо сфокусований елемент
const previouslyFocused = document.activeElement as HTMLElement;
// Переміщаємо фокус у модальне вікно
modalRef.current?.focus();
return () => {
// Повертаємо фокус при закритті
previouslyFocused?.focus();
};
}
}, [isOpen]);
return (
<div
ref={modalRef}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
tabIndex={-1}
>
{children}
</div>
);
};
Перевірка контрастності:
# Використовуйте axe DevTools у браузері — вкладка Elements
# Або програмно:
node -e "
const { getContrastRatio } = require('polished');
const ratio = getContrastRatio('#767676', '#ffffff');
console.log('Ratio:', ratio.toFixed(2), ratio >= 4.5 ? 'PASS' : 'FAIL AA');
"
Типові знахідки та виправлення
Користувацькі компоненти без ARIA:
// ПОГАНО: кнопка-іконка без опису
<div onClick={handleDelete}>
<TrashIcon />
</div>
// ДОБРЕ:
<button
type="button"
onClick={handleDelete}
aria-label="Видалити запис"
>
<TrashIcon aria-hidden="true" />
</button>
Форма без з'єднання label–input:
<!-- ПОГАНО -->
<label>Email</label>
<input type="email" name="email">
<!-- ДОБРЕ -->
<label for="email">Email</label>
<input type="email" id="email" name="email">
<!-- Або через aria-labelledby -->
<span id="email-label">Email</span>
<input type="email" aria-labelledby="email-label">
Пропуск навігації:
<!-- Перший елемент сторінки — посилання для пропуску навігації -->
<a href="#main-content" class="skip-link">
Перейти до основного контенту
</a>
<nav>...</nav>
<main id="main-content">...</main>
.skip-link {
position: absolute;
left: -9999px;
}
.skip-link:focus {
left: 0;
top: 0;
z-index: 9999;
}
Інтеграція в CI/CD
# GitHub Actions
- name: Accessibility Check
run: |
npx pa11y-ci --config .pa11yci --threshold 0
# threshold 0 = жодна помилка WCAG AA не дозволяється
# Або axe у тестах Playwright
- name: Run axe in E2E
run: npx playwright test accessibility.spec.ts
// accessibility.spec.ts
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('Homepage should not have accessibility violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.analyze();
expect(results.violations).toEqual([]);
});
Формат звіту
Після аудиту надається структурований звіт:
- Критичні (блокують роботу клавіатури або скринридера) — потребує негайного виправлення
- Серйозні (нарушення WCAG AA) — виправити протягом спринту
- Помірні (рекомендації WCAG AAA, best practices) — план розвитку
Аудит сайтів до 20 сторінок займає 2–3 робочих дні, включаючи тестування з NVDA та ручну перевірку всіх інтерактивних сценаріїв.







