Реалізація ARIA-атрибутів для доступності сайту
ARIA (Accessible Rich Internet Applications) — набір HTML-атрибутів, які розширюють семантику елементів для допоміжних технологій. Використовується коли нативної HTML-семантики недостатньо — переважно для спеціальних інтерактивних компонентів.
Перше правило ARIA
Не використовувати ARIA, якщо можна обійтись нативним HTML. Нативні елементи вже мають вбудовану доступність:
<!-- Погано — ARIA-костилі на div -->
<div role="button" tabindex="0" aria-pressed="false">Перемикач</div>
<!-- Добре — нативна кнопка -->
<button type="button">Перемикач</button>
<!-- Погано -->
<div role="heading" aria-level="2">Заголовок</div>
<!-- Добре -->
<h2>Заголовок</h2>
Ключові ARIA-атрибути
Ролі:
<div role="alert">Помилка при збереженні</div> <!-- важливе повідомлення -->
<div role="status">Збережено успішно</div> <!-- не критичне сповіщення -->
<nav role="navigation" aria-label="Основна">...</nav> <!-- навігаційний блок -->
<div role="dialog" aria-modal="true">...</div> <!-- модальне вікно -->
<ul role="listbox">...</ul> <!-- список вибору -->
Стани:
<button aria-expanded="false" aria-controls="dropdown-menu">Меню</button>
<ul id="dropdown-menu" aria-hidden="true">...</ul>
<input type="checkbox" aria-checked="mixed"> <!-- проміжний стан -->
<button aria-pressed="true">Жирний</button> <!-- toggle-кнопка -->
<input aria-disabled="true"> <!-- візуально відключено -->
<div aria-busy="true">Завантаження...</div>
Зв'язки між елементами:
<!-- aria-labelledby — заголовок елемента -->
<section aria-labelledby="section-title">
<h2 id="section-title">Про компанію</h2>
</section>
<!-- aria-describedby — додаткове описання -->
<input id="password" type="password"
aria-describedby="password-requirements">
<p id="password-requirements">Мінімум 8 символів, одна цифра</p>
<!-- aria-controls — керує іншим елементом -->
<button aria-controls="panel-1" aria-expanded="false">Показати деталі</button>
<!-- aria-owns — елементи, що не є потомками в DOM -->
<ul role="listbox" aria-owns="option-1 option-2">...</ul>
Live Regions
<!-- Автоматично оголошується екранною читалкою при змінах -->
<div aria-live="polite" aria-atomic="true">
<!-- Ввічливо: оголосити після поточної мови -->
Кошик оновлений: 3 товари
</div>
<div aria-live="assertive">
<!-- Негайно перервати поточну мову — лише для критичних помилок -->
Помилка: не удалося підключитися до сервера
</div>
<!-- role="status" = aria-live="polite" + aria-atomic="true" -->
<p role="status">Зміни збережені</p>
<!-- role="alert" = aria-live="assertive" -->
<p role="alert">Форма містить помилки</p>
Компонент системи сповіщень з ARIA
function NotificationSystem() {
const [notifications, setNotifications] = useState<Notification[]>([]);
return (
<>
{/* Регіон для екранної читалки — завжди в DOM */}
<div
aria-live="polite"
aria-atomic="false"
className="sr-only"
id="sr-notifications"
>
{notifications.map(n => (
<p key={n.id}>{n.message}</p>
))}
</div>
{/* Візуальні сповіщення */}
<div className="toast-container">
{notifications.map(n => (
<Toast key={n.id} notification={n} />
))}
</div>
</>
);
}
Аккордеон
function Accordion({ items }) {
const [openIndex, setOpenIndex] = useState<number | null>(null);
return (
<div>
{items.map((item, i) => (
<div key={item.id}>
<h3>
<button
aria-expanded={openIndex === i}
aria-controls={`panel-${i}`}
id={`header-${i}`}
onClick={() => setOpenIndex(openIndex === i ? null : i)}
>
{item.title}
</button>
</h3>
<div
id={`panel-${i}`}
role="region"
aria-labelledby={`header-${i}`}
hidden={openIndex !== i}
>
{item.content}
</div>
</div>
))}
</div>
);
}
Таблиці даних
<table>
<caption>Статистика продажів за Q3 2024</caption>
<thead>
<tr>
<th scope="col">Продукт</th>
<th scope="col">Продажі</th>
<th scope="col">Зміна</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Продукт A</th>
<td>1 240</td>
<td>
<span aria-label="ріст на 12%">+12%</span>
</td>
</tr>
</tbody>
</table>
Часті помилки
-
aria-hidden="true"на інтерактивних елементах — вони стають недоступні з клавіатури - Дублювання нативної семантики:
<button role="button">— надлишкове -
aria-labelна не семантичних div без role — не працює - Динамічне оновлення
aria-labelбезaria-live— екранна читалка не помітить
Часові рамки
Додавання ARIA на спеціальні компоненти в існуючому проекті: 2–4 дні залежно від кількості компонентів.







