Працює
Академія
Практика

ARIA атрибути для розробників — практичний гайд

Практичний гайд по ARIA атрибутах українською: aria-label, aria-labelledby, role, aria-live, aria-expanded. Приклади коду для кожного атрибута.

· 5 хв читання

ARIA атрибути для розробників — практичний гайд

ARIA (Accessible Rich Internet Applications) — набір атрибутів HTML, що дозволяють передавати семантику та стан інтерактивних компонентів до допоміжних технологій (скрін-рідерів, брайлівських дисплеїв). Специфікація розроблена W3C WAI і є частиною стандарту WCAG.

Коротко: ARIA дозволяє скрін-рідеру "зрозуміти" кастомні компоненти — випадаючі меню, вкладки, accordion, modal-вікна — і правильно їх озвучити.

Факт: Неправильне використання ARIA робить сайт гіршим, ніж без ARIA взагалі. За даними WebAIM Screen Reader Survey, головна скарга — компоненти з помилковою ARIA-семантикою.


Що таке ARIA і навіщо?

ARIA додає три речі до HTML-елементів:

  1. Ролі (roles) — що це за елемент? (button, dialog, tabpanel)
  2. Властивості (properties) — постійні характеристики (aria-label, aria-required)
  3. Стани (states) — динамічні характеристики (aria-expanded, aria-checked, aria-disabled)

Без ARIA скрін-рідер бачить лише голий HTML. З ARIA — він розуміє, що <div class="dropdown"> — це насправді combobox, який зараз розгорнутий (aria-expanded="true") і показує список з 5 опцій (aria-activedescendant).


Перше правило ARIA (не використовуй якщо можна без)

Офіційне перше правило ARIA від W3C:

"Якщо ви можете використати нативний HTML-елемент або атрибут з потрібною семантикою та поведінкою, замість того щоб перевизначати елемент і додавати ARIA роль, стан або властивість — зробіть це."

<!-- ❌ Погано — ARIA де нативний HTML кращий -->
<div role="button" tabindex="0" onclick="submit()">Надіслати</div>
<div role="checkbox" aria-checked="true">Погоджуюсь</div>

<!-- ✅ Добре — нативний HTML -->
<button type="submit">Надіслати</button>
<input type="checkbox" checked id="agree" />
<label for="agree">Погоджуюсь</label>

Нативні HTML-елементи вже мають правильні ролі, стани, фокус і обробку клавіатури. <button> відповідає на Enter і Space, <input type="checkbox"> — на Space, <a> — на Enter. З <div role="button"> вам потрібно реалізовувати все це вручну.


aria-label, aria-labelledby, aria-describedby

Ці три атрибути відповідають на різні питання скрін-рідера:

aria-label — "як це називається?"

Додає текстовий підпис безпосередньо в атрибут. Перевизначає видимий текст елемента.

<!-- Кнопка без видимого тексту (лише іконка) -->
<button aria-label="Закрити діалогове вікно">
  <svg aria-hidden="true" focusable="false">
    <!-- X icon -->
  </svg>
</button>

<!-- Поле пошуку -->
<input type="search" aria-label="Пошук по сайту" placeholder="Введіть запит" />

<!-- Landmark з уточненням -->
<nav aria-label="Головна навігація">...</nav>
<nav aria-label="Навігація у підвалі">...</nav>

Коли використовувати: для елементів без видимого тексту (іконки-кнопки) або коли видимий текст недостатньо описовий.

aria-labelledby — "вказати на видимий заголовок"

Посилається на інший елемент на сторінці як підпис. Перевага: підпис видимий всім користувачам.

<!-- Модальне вікно з заголовком -->
<div role="dialog" aria-labelledby="modal-title" aria-modal="true">
  <h2 id="modal-title">Підтвердження видалення</h2>
  <p>Ви впевнені, що хочете видалити цей файл?</p>
  <button>Видалити</button>
  <button>Скасувати</button>
</div>

<!-- Секція зі заголовком -->
<section aria-labelledby="news-heading">
  <h2 id="news-heading">Останні новини</h2>
  ...
</section>

aria-describedby — "додаткова інформація"

Вказує на елемент, що надає додаткове пояснення (на відміну від підпису).

<!-- Поле з підказкою -->
<label for="password">Пароль</label>
<input type="password" id="password" aria-describedby="password-hint" />
<p id="password-hint">Мінімум 8 символів, одна велика літера і цифра</p>

<!-- Поле з помилкою -->
<label for="email">Email</label>
<input
  type="email"
  id="email"
  aria-invalid="true"
  aria-describedby="email-error"
/>
<p id="email-error" role="alert">Введіть коректну email-адресу</p>

role атрибути

role повідомляє скрін-рідеру, яку функцію виконує елемент.

Найважливіші ролі:

<!-- Навігаційна структура -->
<div role="banner">Шапка сайту</div>
<!-- = <header> -->
<div role="navigation">Меню</div>
<!-- = <nav> -->
<div role="main">Основний контент</div>
<!-- = <main> -->
<div role="contentinfo">Підвал</div>
<!-- = <footer> -->
<div role="complementary">Сайдбар</div>
<!-- = <aside> -->

<!-- Інтерактивні компоненти -->
<div role="dialog" aria-modal="true">Модальне вікно</div>
<div role="alert">Повідомлення про помилку</div>
<div role="status">Успішно збережено</div>

<!-- Складні компоненти -->
<div role="tablist">
  <button role="tab" aria-selected="true" aria-controls="panel1">
    Вкладка 1
  </button>
  <button role="tab" aria-selected="false" aria-controls="panel2">
    Вкладка 2
  </button>
</div>
<div role="tabpanel" id="panel1">Вміст вкладки 1</div>

Важливо: role="presentation" або role="none" прибирає семантику елемента (для декоративних таблиць, наприклад):

<table role="presentation">
  <!-- Таблиця для верстки, не для даних — прибираємо семантику таблиці -->
</table>

aria-live для динамічного контенту

Коли контент змінюється без перезавантаження сторінки, скрін-рідер не знає про це. aria-live робить регіон "живим" — зміни оголошуються автоматично.

<!-- Повідомлення про успіх/помилку після відправки форми -->
<div aria-live="polite" aria-atomic="true" id="form-status">
  <!-- JS оновлює цей контент: -->
  <!-- "Форму успішно відправлено" -->
</div>

<!-- Лічильник символів -->
<textarea id="message" maxlength="280"></textarea>
<p aria-live="polite"><span id="char-count">0</span>/280 символів</p>

<!-- Критичні повідомлення (переривають поточне читання) -->
<div aria-live="assertive" role="alert">
  Сеанс закінчується через 2 хвилини. Збережіть дані.
</div>

Значення aria-live:

  • polite — оголошується після завершення поточного читання (для більшості оновлень)
  • assertive — переривається і оголошується негайно (лише для критичних сповіщень)
  • off — вимкнено (за замовчуванням)

aria-atomic="true" — весь регіон читається заново при будь-якій зміні (рекомендовано для статус-повідомлень).


aria-expanded, aria-controls для інтерактивних елементів

Accordion / Dropdown

<!-- Accordion -->
<button
  aria-expanded="false"
  aria-controls="section-content"
  id="section-toggle"
>
  Розкрийте розділ
</button>
<div id="section-content" hidden>
  <p>Вміст розділу...</p>
</div>
// JS для перемикання
const btn = document.getElementById("section-toggle");
const content = document.getElementById("section-content");

btn.addEventListener("click", () => {
  const isExpanded = btn.getAttribute("aria-expanded") === "true";
  btn.setAttribute("aria-expanded", !isExpanded);
  content.hidden = isExpanded;
});

Навігаційне меню з підменю

<nav aria-label="Головна навігація">
  <ul>
    <li>
      <button
        aria-expanded="false"
        aria-haspopup="true"
        aria-controls="products-menu"
      >
        Продукти
      </button>
      <ul id="products-menu" hidden role="menu">
        <li role="menuitem"><a href="/audit">Аудит</a></li>
        <li role="menuitem"><a href="/rankings">Рейтинги</a></li>
      </ul>
    </li>
  </ul>
</nav>

Модальне вікно (повний приклад)

<!-- Trigger -->
<button aria-haspopup="dialog" onclick="openModal()">Відкрити деталі</button>

<!-- Modal -->
<div
  id="modal"
  role="dialog"
  aria-modal="true"
  aria-labelledby="modal-title"
  aria-describedby="modal-desc"
  hidden
>
  <h2 id="modal-title">Заголовок діалогу</h2>
  <p id="modal-desc">Опис дії або змісту діалогу.</p>
  <button>Підтвердити</button>
  <button onclick="closeModal()">Скасувати</button>
</div>
function openModal() {
  const modal = document.getElementById("modal");
  modal.hidden = false;
  // Перемістити фокус всередину модалки
  modal.querySelector("button").focus();
  // Заблокувати скрол фону
  document.body.style.overflow = "hidden";
  // Escape закриває
  document.addEventListener("keydown", handleEscape);
}

function handleEscape(e) {
  if (e.key === "Escape") closeModal();
}

Поширені питання про ARIA

Чи замінює ARIA семантичний HTML? Ні. ARIA доповнює HTML там, де нативних елементів недостатньо. Завжди надавайте перевагу нативному HTML — <button>, <input>, <select>, <nav>, <main>.

Що буде якщо я неправильно використаю ARIA? Скрін-рідер може озвучити неправильну роль або стан. Наприклад, role="button" на <div> без tabindex="0" і обробника клавіатури — недоступний. Неправильний aria-expanded — користувач не знає, відкрито підменю чи ні.

Скільки ARIA атрибутів потрібно для типового сайту? Для більшості сайтів достатньо: aria-label для іконок, aria-expanded для меню/accordion, aria-live для динамічних оновлень, aria-hidden для декоративних елементів. Решта — для складних кастомних компонентів.


Корисні посилання

Чи була ця стаття корисною?