Пошук уроків, статей та іншого контенту
Знайомить із базовими принципами доступності: структурою заголовків, навігацією з клавіатури, назвами й ролями елементів.
Доступність у HTML — це не набір спеціальних атрибутів, а правильне використання вбудованої семантики документа.
Браузер будує для допоміжних технологій, зокрема screen reader, дерево доступності. У ньому важливі:
роль елемента: що це — кнопка, посилання, заголовок, навігація;
доступне ім’я: як елемент називається для користувача;
стан і властивості: наприклад, чи кнопка вимкнена або чи поле позначене як помилкове;
порядок обходу: у якій послідовності елементи отримують фокус.
Найнадійніший спосіб створити доступний інтерфейс — починати з правильних HTML-елементів, а не відтворювати їхню поведінку за допомогою div, CSS і JavaScript.
<button type="button">Відкрити меню</button>
<a href="/profile">Мій профіль</a>Ці два елементи мають різне призначення:
button запускає дію на поточній сторінці;
a переходить до ресурсу або URL.
Не варто замінювати їх на:
<div onclick="openMenu()">Відкрити меню</div>
<div onclick="location.href='/profile'">Мій профіль</div>Такий код не має відповідної нативної ролі, зазвичай не працює з клавіатури та не передає допоміжним технологіям очікувану інформацію.
Заголовки формують ієрархію документа:
h1 — назва всієї сторінки;
h2 — основний розділ;
h3 — підрозділ усередині h2;
h4 та наступні рівні — вкладені підрозділи.
Заголовки потрібні не лише для візуального оформлення. Користувач screen reader може переглядати список заголовків і швидко переходити між розділами.
На сторінці зазвичай має бути один основний h1, який описує її призначення. Далі рівні заголовків мають відображати структуру змісту:
<h1>Налаштування облікового запису</h1>
<h2>Особисті дані</h2>
<h3>Контактна інформація</h3>
<h3>Часовий пояс</h3>
<h2>Безпека</h2>
<h3>Пароль</h3>
<h3>Двофакторна автентифікація</h3>Не слід пропускати рівні лише заради потрібного розміру шрифту:
<h1>Налаштування</h1>
<h4>Особисті дані</h4>Якщо потрібен певний візуальний розмір, його змінюють за допомогою CSS, не порушуючи структуру заголовків.
Заголовок має описувати зміст наступного розділу. Не використовуйте заголовки як декоративні елементи або замість звичайного тексту.
Для логічних частин сторінки використовуйте семантичні елементи:
<header>
<h1>Панель керування</h1>
</header>
<main>
<section aria-labelledby="projects-title">
<h2 id="projects-title">Мої проєкти</h2>
<p>Список активних проєктів.</p>
</section>
</main>
<footer>
<p>© 2026 Компанія</p>
</footer>section бажано використовувати для тематично окремої частини, яка має власний заголовок. Якщо фрагмент не має окремої теми або заголовка, звичайний div може бути доречнішим.
main позначає головний унікальний вміст сторінки. На сторінці має бути один основний main.
header і footer можуть належати як усій сторінці, так і окремій секції. Їхня наявність сама по собі не замінює заголовок секції.
Основну навігацію розміщуйте в nav. Атрибут aria-label допомагає розрізняти кілька навігаційних областей:
<nav aria-label="Основна навігація">
<ul>
<li><a href="/">Головна</a></li>
<li><a href="/courses">Курси</a></li>
<li><a href="/profile">Профіль</a></li>
</ul>
</nav>
<nav aria-label="Навігація сторінкою">
<ul>
<li><a href="#overview">Огляд</a></li>
<li><a href="#requirements">Вимоги</a></li>
</ul>
</nav>Посилання повинно мати зрозумілий доступний текст і реальну адресу:
<a href="/reports">Переглянути звіти</a>Небажаний варіант:
<a href="/reports">Натисніть тут</a>Якщо на сторінці кілька однакових текстів посилань, користувачу складно зрозуміти їх призначення поза контекстом. Краще уточнити текст:
<ul>
<li>
<span>Звіт за січень</span>
<a href="/reports/january">Переглянути звіт за січень</a>
</li>
<li>
<span>Звіт за лютий</span>
<a href="/reports/february">Переглянути звіт за лютий</a>
</li>
</ul>Користувач повинен мати змогу виконати всі основні дії без миші.
Інтерактивні нативні елементи зазвичай автоматично беруть участь у послідовності фокусу:
посилання з href;
button;
поля форми;
елементи керування формами.
Не прибирайте видимий фокус:
/* Погано: користувач не бачить поточне місце фокусу */
*:focus {
outline: none;
}Натомість створіть достатньо помітний стиль:
:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 3px;
}:focus-visible дає змогу стилізувати фокус, коли він справді потрібен для орієнтації, зокрема під час навігації клавіатурою.
За замовчуванням порядок фокусу відповідає порядку елементів у DOM. Це зазвичай найкращий варіант, оскільки він узгоджується з візуальним і логічним порядком.
Не використовуйте позитивні значення tabindex:
<!-- Погано: створює крихкий ручний порядок фокусу -->
<a href="/first" tabindex="3">Перший розділ</a>
<a href="/second" tabindex="1">Другий розділ</a>Позитивний tabindex змушує підтримувати окремий порядок, який легко зламати після зміни розмітки.
Значення мають різне призначення:
без tabindex — нативна поведінка елемента;
tabindex="0" — додає нефокусованому за замовчуванням елементу участь у природному порядку фокусу;
tabindex="-1" — дозволяє встановити фокус програмно, але не додає елемент до послідовності переходу клавішею Tab.
tabindex="-1" часто корисний для заголовка області, до якої потрібно перейти після зміни стану:
<h2 id="result-title" tabindex="-1">Результат пошуку</h2>Утім, не додавайте tabindex без конкретної потреби. Спочатку перевірте, чи підходить нативний інтерактивний елемент.
button підтримує очікувану клавіатурну поведінку: його можна сфокусувати та активувати клавішею Enter або пробілом.
Якщо замість нього використати div, доведеться вручну додавати:
роль;
можливість фокусу;
обробку Enter;
обробку пробілу;
правильне керування станом;
додаткову перевірку взаємодії з допоміжними технологіями.
Тому правильний вибір такий:
<button type="button" id="toggle-details" aria-expanded="false">
Показати деталі
</button>
<div id="details" hidden>
Додаткова інформація про замовлення.
</div>Доступна назва — це текст, яким допоміжна технологія представляє елемент користувачу.
Наприклад, для кнопки доступною назвою може бути її текст:
<button type="button">Зберегти зміни</button>Для поля форми доступну назву найкраще задавати через пов’язаний label:
<label for="email">Електронна пошта</label>
<input id="email" name="email" type="email">Зв’язок створюється через однакові значення for і id. Клік по тексту також переводить фокус у поле.
Не покладайтеся лише на placeholder:
<input
id="email"
name="email"
type="email"
placeholder="name@example.com"
>placeholder — це підказка або приклад значення, а не повноцінна назва поля. Він зникає після введення тексту та часто має недостатній контраст.
Для кнопки з іконкою без тексту потрібна явна назва:
<button type="button" aria-label="Закрити діалог">
<span aria-hidden="true">×</span>
</button>aria-label не слід додавати без потреби. Якщо видимий текст уже правильно називає елемент, він зазвичай є кращим джерелом доступної назви:
<button type="button" aria-label="Видалити файл">
Видалити
</button>У цьому прикладі aria-label замінює видимий текст для допоміжних технологій, тому його значення має бути узгоджене з написом на екрані. Без спеціальної потреби краще написати:
<button type="button">Видалити файл</button>Роль описує тип елемента для дерева доступності. Нативні HTML-елементи вже мають ролі:
button — кнопка;
a href="..." — посилання;
nav — область навігації;
main — головна область;
h1–h6 — заголовки відповідного рівня;
input — елемент керування певного типу.
ARIA допомагає доповнювати семантику, але не робить довільний елемент повноцінною заміною нативному:
<div role="button">Зберегти</div>Такий div все ще не отримує автоматично повної поведінки кнопки. У нього немає гарантованої клавіатурної активації, а автор має самостійно синхронізувати всі стани.
Правило практичного вибору:
Знайдіть нативний HTML-елемент для потрібної дії.
Використайте його без зайвих ARIA-атрибутів.
Додавайте ARIA лише тоді, коли нативної семантики недостатньо.
Якщо додаєте роль і стан, забезпечте також відповідну клавіатурну поведінку.
ARIA не повинна суперечити нативній семантиці. Наприклад, не варто без причини перепризначати роль кнопки як посилання або роль заголовка як звичайного тексту.
Для динамічних компонентів важливо повідомляти не лише роль, а й поточний стан.
У прикладі нижче:
aria-expanded="false" повідомляє, що панель закрита;
hidden фактично приховує панель від візуального відображення та дерева доступності;
aria-controls пов’язує кнопку з панеллю, якою вона керує.
<button
type="button"
id="toggle-details"
aria-expanded="false"
aria-controls="details"
>
Показати деталі
</button>
<section id="details" hidden aria-labelledby="details-title">
<h2 id="details-title">Деталі замовлення</h2>
<p>Замовлення буде оброблено протягом одного робочого дня.</p>
</section>JavaScript має оновлювати всі пов’язані стани одночасно:
<!doctype html>
<html lang="uk">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Доступна панель деталей</title>
<style>
:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 3px;
}
button {
padding: 0.6rem 1rem;
font: inherit;
cursor: pointer;
}
section {
max-width: 32rem;
margin-top: 1rem;
padding: 1rem;
border: 1px solid #777;
}
</style>
</head>
<body>
<main>
<h1>Замовлення №1042</h1>
<button
type="button"
id="toggle-details"
aria-expanded="false"
aria-controls="details"
>
Показати деталі
</button>
<section id="details" hidden aria-labelledby="details-title">
<h2 id="details-title">Деталі замовлення</h2>
<p>Замовлення передано в обробку.</p>
<p>Орієнтовна дата доставки: 12 серпня.</p>
</section>
</main>
<script>
const button = document.querySelector('#toggle-details');
const details = document.querySelector('#details');
button.addEventListener('click', () => {
const isExpanded = button.getAttribute('aria-expanded') === 'true';
button.setAttribute('aria-expanded', String(!isExpanded));
button.textContent = isExpanded
? 'Показати деталі'
: 'Приховати деталі';
details.hidden = isExpanded;
});
</script>
</body>
</html>Текст кнопки змінюється разом зі значенням aria-expanded, тому візуальна назва та оголошений стан не суперечать одне одному.
Нижче наведено приклад, у якому поєднано заголовки, області сторінки, навігацію, доступні назви та нативні елементи керування:
<!doctype html>
<html lang="uk">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Налаштування профілю</title>
<style>
body {
max-width: 48rem;
margin: 0 auto;
padding: 1rem;
font: 1rem/1.5 system-ui, sans-serif;
}
nav ul {
display: flex;
gap: 1rem;
padding: 0;
list-style: none;
}
form {
display: grid;
gap: 1rem;
max-width: 30rem;
}
label {
display: grid;
gap: 0.25rem;
font-weight: 600;
}
input,
button {
width: fit-content;
padding: 0.5rem;
font: inherit;
}
:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 3px;
}
</style>
</head>
<body>
<header>
<h1>Налаштування профілю</h1>
<nav aria-label="Основна навігація">
<ul>
<li><a href="/">Головна</a></li>
<li><a href="/projects">Проєкти</a></li>
<li><a href="/settings" aria-current="page">Налаштування</a></li>
</ul>
</nav>
</header>
<main>
<section aria-labelledby="personal-data-title">
<h2 id="personal-data-title">Особисті дані</h2>
<form action="/profile" method="post">
<label for="full-name">
Повне ім’я
<input
id="full-name"
name="fullName"
type="text"
autocomplete="name"
required
>
</label>
<label for="email">
Електронна пошта
<input
id="email"
name="email"
type="email"
autocomplete="email"
required
>
</label>
<button type="submit">Зберегти зміни</button>
</form>
</section>
</main>
<footer>
<p>Поля, позначені як обов’язкові, потрібно заповнити.</p>
</footer>
</body>
</html>У цьому прикладі:
lang="uk" повідомляє мову основного вмісту;
h1 описує сторінку;
nav має назву області;
поточне посилання позначене aria-current="page";
main, section і footer формують структуру сторінки;
кожне поле має видиму назву через label;
кнопка форми є нативним button;
видимий фокус не вимкнено;
порядок DOM відповідає логічному порядку читання.
Після створення сторінки перевірте її без миші:
Перезавантажте сторінку.
Натискайте Tab і стежте, чи видно поточний фокус.
Перевірте, чи порядок фокусу відповідає змісту.
Активуйте посилання клавішею Enter.
Активуйте кнопки клавішею Enter і пробілом.
Перевірте, чи можна дістатися кожного важливого елемента.
Переконайтеся, що закриті області не отримують фокус.
Перевірте, чи зміни стану оголошуються зрозуміло: наприклад, «розгорнуто» або «згорнуто».
Окремо перевірте сторінку після збільшення масштабу та в режимі вузького вікна. Зміна розміру тексту не повинна робити керування недоступним.
div замість інтерактивного елемента<div class="button">Надіслати</div>Клас не створює роль, фокус і клавіатурну поведінку. Використовуйте:
<button type="submit">Надіслати</button>button:focus {
outline: none;
}Якщо стандартний контур не підходить, замініть його власним помітним стилем, а не прибирайте повністю.
tabindex<input tabindex="1">
<button tabindex="2">Готово</button>Такий порядок складно підтримувати. Розміщуйте елементи в потрібному порядку в DOM і не використовуйте позитивні значення tabindex.
<input type="text" name="city">Placeholder або зовнішній текст без зв’язку з полем не є надійною назвою. Додайте label:
<label for="city">Місто</label>
<input id="city" type="text" name="city"><h3>Великий декоративний текст</h3>Якщо це не заголовок розділу, використовуйте звичайний елемент і CSS для оформлення.
Якщо панель візуально відкрита, але кнопка все ще має aria-expanded="false", користувачі отримують суперечливу інформацію. Візуальний стан, DOM-стан і ARIA-стан повинні оновлюватися разом.
Семантичний HTML створює правильні ролі та стандартну поведінку без зайвого JavaScript.
Ієрархія h1–h6 повинна відображати структуру змісту, а не лише розмір шрифту.
Основні частини сторінки варто позначати через header, nav, main, section і footer.
Для дій використовуйте button, для переходів — a з href.
Усі важливі дії мають бути доступними з клавіатури та мати видимий фокус.
Поля форм повинні мати пов’язані label.
Доступна назва має чітко описувати призначення елемента.
ARIA доповнює нативний HTML, але не замінює його.
aria-expanded, aria-current та інші стани потрібно синхронізувати з фактичним станом інтерфейсу.
Найкраща перевірка починається з навігації клавіатурою та аналізу логічної структури документа.