Почему семантический HTML — основа доступности
Пошаговый гайд по семантическому HTML: правильное использование тегов, иерархия заголовков и как избежать div-супа.
Андрій Вдовин
Семантичний HTML — це використання HTML-елементів відповідно до їхнього призначення: <button> для кнопок, <nav> для навігації, <article> для самостійних публікацій. На відміну від суто презентаційних елементів (як-от <div> і <span>), семантичні елементи несуть змістове навантаження, зрозуміле не лише людям, а й браузерам, пошуковим роботам та асистивним технологіям. Зв'язок із доступністю тут прямий: коли розмітка передає структуру і призначення контенту, скрінрідери можуть правильно оголосити цей контент, а користувачі клавіатури — ефективно ним керувати. Це фундамент, на якому будується будь-яка доступна веб-сторінка.
Div soup vs семантичний HTML
«Div soup» — це жаргонний термін для сторінок, де весь контент загорнутий у безліч вкладених <div>, без жодної структурної розмітки. Такий підхід може виглядати ідентично для зрячого користувача, але для скрінрідера це суцільний потік тексту без орієнтирів.
❌ Неправильно — «div soup»
<div class="header">
<div class="logo">InclusiveWeb</div>
<div class="nav">
<div class="nav-item">Продукт</div>
<div class="nav-item">Ціни</div>
</div>
</div>
<div class="main">
<div class="hero">
<div class="title">Зробіть сайт доступним</div>
<div class="btn" onclick="signup()">Почати</div>
</div>
<div class="content">
<div class="post">
<div class="post-title">ARIA помилки</div>
<div class="post-text">Стаття...</div>
</div>
</div>
</div>
<div class="footer">© 2025 InclusiveWeb</div>✅ Правильно — семантичні landmarks
<header>
<a href="/" aria-label="InclusiveWeb — головна">
<img src="/logo.svg" alt="InclusiveWeb" />
</a>
<nav aria-label="Головна навігація">
<ul>
<li><a href="/product">Продукт</a></li>
<li><a href="/pricing">Ціни</a></li>
</ul>
</nav>
</header>
<main>
<section aria-labelledby="hero-heading">
<h1 id="hero-heading">Зробіть сайт доступним</h1>
<button type="button" onclick="signup()">Почати</button>
</section>
<section aria-labelledby="posts-heading">
<h2 id="posts-heading">Останні статті</h2>
<article>
<h3>ARIA помилки</h3>
<p>Стаття...</p>
</article>
</section>
</main>
<footer>
<p>© 2025 InclusiveWeb</p>
</footer>Коли скрінрідер потрапляє на семантичну сторінку, користувач може одразу перейти до <main>, перелистати заголовки або перейти по landmark-регіонах. У «div soup» нічого цього немає.
Semantic HTML та Accessibility Tree
Браузер будує дві паралельні структури з HTML-коду: DOM-дерево (Document Object Model) для рендерингу та дерево доступності (Accessibility Tree) для асистивних технологій. Дерево доступності — це спрощена версія DOM, де кожен вузол описується чотирма властивостями: роль (що це за елемент), ім'я (як він називається), стан (наприклад, checked, expanded) та значення.
Семантичні елементи автоматично отримують правильні ролі в дереві доступності: <button> отримує роль button, <nav> — роль navigation, <input type="checkbox"> — роль checkbox з відповідним станом checked. Натомість <div> потрапляє в дерево доступності без ролі — просто як безіменний блок, якщо не додати ARIA вручну.
Як переглянути Accessibility Tree
У Chrome DevTools відкрийте вкладку Elements, потім у правій панелі знайдіть вкладку «Accessibility». Там ви побачите, як браузер інтерпретує кожен елемент: його роль, ім'я та стан. Це незамінний інструмент для діагностики проблем доступності.
Ключові семантичні елементи
Нижче — огляд найважливіших семантичних елементів HTML, їхніх ARIA-ролей у дереві доступності та рекомендацій щодо використання.
| Елемент | ARIA роль | Коли використовувати |
|---|---|---|
| <button> | button | Будь-яка інтерактивна дія (без навігації) |
| <nav> | navigation | Набори навігаційних посилань (меню, хлібні крихти) |
| <main> | main | Основний унікальний контент сторінки (один на сторінку) |
| <header> | banner (якщо в body) | Шапка сайту або секції: логотип, заголовок, навігація |
| <footer> | contentinfo (якщо в body) | Підвал сайту: копірайт, контакти, додаткові посилання |
| <article> | article | Самостійний контент: пост, стаття, коментар, картка товару |
| <section> | region (з aria-label) | Тематична секція документа з заголовком |
| <aside> | complementary | Бічна панель, додатковий контент, пов'язані матеріали |
| <figure> | figure | Зображення, діаграми, код з підписом figcaption |
| <time> | time | Дати і часові відмітки з атрибутом datetime |
Ієрархія заголовків
Заголовки <h1>–<h6> — це не просто великий або жирний текст. Вони створюють ієрархічну структуру документа — як зміст книги. Для користувачів скрінрідерів заголовки є основним засобом навігації: більшість з них переміщуються сторінкою, натискаючи клавішу H (наступний заголовок) або 1–6 (заголовок певного рівня).
Правило просте: ніколи не пропускайте рівні заголовків. Після <h1> може йти лише <h2>, після <h2> — <h3> або наступний <h2>. Вибір розміру шрифта — завдання CSS, а не рівня заголовка.
❌ Неправильно
<h1>InclusiveWeb</h1>
<!-- Пропущено h2 — стрибок на h4! -->
<h4>Наші послуги</h4>
<!-- h3 використовується для стилізації, не структури -->
<h3>Детальніше</h3>
<!-- Кілька h1 на сторінці -->
<h1>Контакти</h1>✅ Правильно
<h1>InclusiveWeb — доступність для всіх</h1>
<h2>Наші послуги</h2>
<h3>Аудит доступності</h3>
<h3>Автоматичне тестування</h3>
<h2>Чому це важливо</h2>
<h3>Юридичні вимоги</h3>
<h3>Бізнес-переваги</h3>
<h2>Контакти</h2>Landmark Navigation
Landmarks (орієнтири) — це ключові регіони сторінки, між якими користувачі скрінрідерів можуть переміщуватись напряму. В NVDA і JAWS це клавіша D, у VoiceOver — W. Це схоже на «швидкі посилання» для навігації сторінкою без необхідності прослуховувати весь вміст зверху донизу.
Основні landmark-елементи:
<header>— шапка сайту, оголошується як «banner»<nav>— навігаційний регіон, оголошується як «navigation»; якщо на сторінці кілька<nav>— кожен має унікальнийaria-label<main>— основний контент сторінки, оголошується як «main»; має бути лише один<aside>— допоміжний контент, оголошується як «complementary»<footer>— підвал сторінки, оголошується як «contentinfo»
<body>
<header> <!-- landmark: banner -->
<nav aria-label="Головна"> <!-- landmark: navigation -->
<ul>...</ul>
</nav>
</header>
<main> <!-- landmark: main -->
<nav aria-label="Хлібні крихти"> <!-- landmark: navigation -->
<ol>...</ol>
</nav>
<article>
<h1>Назва статті</h1>
<aside aria-label="Авторська нотатка"> <!-- landmark: complementary -->
<p>Примітка автора...</p>
</aside>
</article>
</main>
<aside aria-label="Схожі статті"> <!-- landmark: complementary -->
<h2>Читайте також</h2>
</aside>
<footer> <!-- landmark: contentinfo -->
<p>© 2025 InclusiveWeb</p>
</footer>
</body><button> vs <div onClick>
Це одна з найчастіших помилок у сучасних JavaScript-застосунках: використання <div> або <span> з обробником onClick замість нативного <button>. Подивімось на детальну різницю:
| Властивість | <button> | <div onClick> |
|---|---|---|
| Клавіатурний фокус | ✓ Автоматично | ✗ Потрібен tabindex="0" |
| Enter / Space | ✓ Вбудовано | ✗ Потрібен keydown |
| ARIA роль | ✓ button (автоматично) | ✗ Потрібен role="button" |
| Стан disabled | ✓ Атрибут disabled | ✗ Потрібен aria-disabled + логіка |
| Стилі :focus-visible | ✓ Підтримується браузером | ✗ Без tabindex не працює |
| type="submit" у формі | ✓ Надсилає форму | ✗ Не надсилає форму |
Semantic forms: label, fieldset, legend
Форми є одним із найскладніших для доступності компонентів. Три семантичні елементи вирішують більшість проблем:
<label> — мітка для поля вводу
Кожне поле форми повинно мати <label>. Це забезпечує три переваги: скрінрідер оголошує мітку при фокусі, область кліку збільшується (клік на текст мітки фокусує поле), а тип поля зрозумілий без візуального контексту.
<fieldset> і <legend> — групування полів
Для групп пов'язаних полів (радіо-кнопки, чекбокси) використовуйте <fieldset> з <legend>. Скрінрідер оголошує текст legend перед кожним полем групи, надаючи контекст.
<form>
<!-- Явна мітка через for/id -->
<div>
<label for="full-name">Повне ім'я</label>
<input type="text" id="full-name" name="name"
autocomplete="name" required />
</div>
<!-- Прихована мітка (видима лише для скрінрідерів) -->
<div>
<label for="search" class="sr-only">Пошук</label>
<input type="search" id="search" placeholder="Пошук..." />
</div>
<!-- Групування радіо-кнопок -->
<fieldset>
<legend>Оберіть план підписки</legend>
<label>
<input type="radio" name="plan" value="free" /> Безкоштовний
</label>
<label>
<input type="radio" name="plan" value="pro" /> Професійний
</label>
<label>
<input type="radio" name="plan" value="enterprise" /> Enterprise
</label>
</fieldset>
<!-- Поле з підказкою через aria-describedby -->
<div>
<label for="password">Пароль</label>
<input type="password" id="password"
aria-describedby="password-hint" required />
<p id="password-hint" class="hint">
Мінімум 8 символів, одна велика літера та цифра
</p>
</div>
<button type="submit">Зареєструватись</button>
</form>Ключовий принцип
Якщо елемент є кнопкою — використовуй <button>. Завжди. Якщо елемент є посиланням — використовуй <a href>. Якщо елемент є навігацією — використовуй <nav>. Нативна семантика безкоштовна, надійна і не потребує ARIA.
Часті питання
Чи впливає семантичний HTML на SEO?
Так, і досить суттєво. Google використовує HTML-структуру для розуміння контенту сторінки. Правильна ієрархія заголовків допомагає визначити головну тему (h1) і підтеми (h2, h3). Елементи <article> і <section> допомагають Google зрозуміти межі контентних блоків. Крім того, семантичний HTML часто дозволяє отримувати розширені сніпети в пошуку (featured snippets).
Чи потрібно переписувати весь старий код, щоб зробити сайт доступним?
Не обов'язково. Почніть з найбільш критичних змін: додайте <main>, <nav> і <header>, виправте ієрархію заголовків, замініть <div onClick> на <button>, додайте мітки до форм. Ці зміни забезпечать 80% покращень при 20% зусиль. Повний рефакторинг можна робити поступово — компонент за компонентом.
Яка різниця між <section> і <article>?
<article> — це самостійний, незалежний контент, який має сенс сам по собі навіть поза контекстом сторінки: пост блогу, новинна стаття, коментар, картка продукту. <section> — це тематична частина документа, яка є частиною більшого цілого і зазвичай має заголовок. Простий тест: чи має сенс цей контент, якщо вирвати його з контексту сторінки? Якщо так — це <article>.
Як перевірити семантичну структуру сторінки?
Декілька ефективних методів: 1) Перевірте сторінку у скрінрідері (NVDA безкоштовний для Windows, VoiceOver вбудований у macOS/iOS) — спробуйте навігацію по заголовках і landmarks. 2) Використайте розширення HeadingsMap або Accessibility Insights для Chrome, щоб побачити структуру заголовків. 3) Запустіть axe DevTools для автоматичного виявлення проблем. 4) Перегляньте дерево доступності у Chrome DevTools (Elements → Accessibility). 5) Спробуйте відключити CSS — добре структурований семантичний HTML має мати логічний порядок і без стилів.
Джерела
- HTML Living Standard — W3C/WHATWG — офіційна специфікація HTML-елементів та їхньої семантики
- MDN Web Docs: Semantics in HTML — доступний посібник із семантичного HTML від Mozilla
- WebAIM: Semantic Structure — практичний посібник WebAIM з семантичної структури та доступності
- W3C WAI: Page Structure Tutorial — офіційний туторіал W3C по структурі сторінки та landmark-регіонах
- Web Content Accessibility Guidelines (WCAG) 2.1 — міжнародний стандарт доступності веб-контенту