HTML & CSS

Семантичні елементи HTML5

Повний розбір семантичних елементів HTML5 за специфікацією WHATWG: <article>, <section>, <nav>, <aside>, <header>, <footer>, <main>, <address>, <time>, <details>, <summary>, <dialog> та інші. Різниця між div і семантичними елементами, доступність та ARIA.

Семантичні елементи HTML5

Від «супу тегів» до структурованого документа

2008 рік. Типова HTML-сторінка того часу виглядала приблизно так:

<div id="header">...</div>
<div id="nav">...</div>
<div id="content">
    <div id="main">
        <div class="post">...</div>
        <div class="post">...</div>
    </div>
    <div id="sidebar">...</div>
</div>
<div id="footer">...</div>

Пошукові системи, скринрідери та браузери бачили однаковий «суп» з <div>. Де тут навігація? Де головний контент? Де підвал? — Ніхто не знав, окрім розробника, який дивиться на id="nav".

У 2014 році вийшов офіційний стандарт HTML5. Один із його ключових внесків — семантичні елементи: теги, що несуть змістове навантаження не лише через атрибути, але й самою своєю назвою.

Сьогодні ми розберемо ці елементи детально — так, як вони описані у HTML Living Standard (WHATWG), а не у спрощених туторіалах.


Що таке «семантика» в HTML

Семантика (від грецького σημαντικός — значущий) — це відповідність між виглядом і змістом. У HTML вона означає: вибір елемента, що точно описує природу вмісту.

Синтаксис

Що написано: <div class="article">, <span class="time">

Браузер бачить прямокутний блок і рядковий елемент. Жодного контексту.

Семантика

Що це означає: <article>, <time>

Браузер, пошуковик, скринрідер розуміють: це стаття, це момент часу.

Навіщо семантика:

  1. Пошукова оптимізація: Google краще розуміє структуру — де заголовок, де основний контент, де навігація
  2. Доступність: скринрідери JAWS, NVDA, VoiceOver орієнтуються по семантичній структурі. <nav> дозволяє перейти одразу до навігації клавіатурою
  3. Maintainability: код із <header> та <article> легше читати і підтримувати, ніж <div class="hdr"> та <div class="art">
  4. Читальний режим: браузери (Safari, Firefox) використовують семантику для «Reader Mode» — вичавлюють основний контент зі сторінки

Категорії вмісту за WHATWG

Перш ніж розглядати окремі елементи, важливо зрозуміти Content Categories (категорії вмісту) зі специфікації. Кожен семантичний елемент належить до певних категорій і може містити лише певні категорії:

Loading diagram...
graph TD
    FC["Flow Content<br/>(більшість елементів тіла)"]
    PC["Phrasing Content<br/>(текстові елементи)"]
    EC["Embedded Content<br/>(img, video, iframe)"]
    IC["Interactive Content<br/>(a, button, input)"]
    HC["Heading Content<br/>(h1–h6, hgroup)"]
    SC["Sectioning Content<br/>(article, section, nav, aside)"]
    MC["Metadata Content<br/>(head: meta, link, script)"]

    FC --> PC
    FC --> EC
    FC --> IC
    FC --> HC
    FC --> SC
  • Sectioning content — елементи, що створюють новий розділ документа: <article>, <section>, <nav>, <aside>
  • Heading content — заголовки розділів: <h1><h6>, <hgroup>
  • Flow content — майже всі елементи, що можуть з'являтися в тілі документа
  • Phrasing content — текстові елементи, можуть іти всередині параграфів

<header> — заголовок секції або сторінки

Специфікація WHATWG

<header> (header — заголовок) представляє вступний вміст або навігаційні засоби для свого батьківського секційного елемента.

  • Content model: flow content, але без вкладених <header>, <footer>, <main>
  • Permitted parents: будь-який елемент, що приймає flow content
  • Implicit ARIA role: banner (якщо не вкладений в секційний елемент), generic (якщо всередині <article>, <aside>, <nav>, <section>)
Ключовий нюанс: <header> може бути не лише на рівні сторінки, але й всередині <article>, <section> — і тоді він є заголовком саме цього розділу, а не всієї сторінки.
<!-- 1. Header сторінки — role="banner" -->
<body>
    <header>
        <a href="/">
            <img src="/logo.svg" alt="Повернутися на головну — TechUA" width="160" height="50" />
        </a>
        <nav aria-label="Головне меню">
            <ul>
                <li><a href="/products">Продукти</a></li>
                <li><a href="/blog">Блог</a></li>
                <li><a href="/about">Про нас</a></li>
            </ul>
        </nav>
        <form role="search" action="/search" method="get">
            <label for="site-search">Пошук по сайту</label>
            <input type="search" id="site-search" name="q" placeholder="Пошук..." />
            <button type="submit">🔍</button>
        </form>
    </header>
    <!-- ... -->
</body>

<!-- 2. Header статті — role="generic" -->
<article>
    <header>
        <h2>React Server Components: рік у продакшні</h2>
        <p>
            Автор: <a href="/authors/ivan">Іван Коваленко</a> · <time datetime="2024-10-15">15 жовтня 2024</time> ·
            <span>Час читання: 12 хв</span>
        </p>
        <ul aria-label="Теги статті">
            <li><a href="/tags/react">React</a></li>
            <li><a href="/tags/nextjs">Next.js</a></li>
        </ul>
    </header>
    <p>Minу рік відтоді як...</p>
</article>

<footer> (footer — підвал) представляє завершальний вміст: інформацію про автора, копірайт, посилання на пов'язані документи, дрібний друк.

  • Implicit ARIA role: contentinfo (якщо нащадок <body>), generic (якщо нащадок секційного елемента)
<!-- Footer сторінки -->
<footer>
    <nav aria-label="Підвальна навігація">
        <ul>
            <li><a href="/privacy">Конфіденційність</a></li>
            <li><a href="/terms">Умови використання</a></li>
            <li><a href="/sitemap">Карта сайту</a></li>
        </ul>
    </nav>

    <address>
        <p>ТОВ «TechUA», вул. Хрещатик 1, Київ, 01001</p>
        <p>Email: <a href="mailto:info@techua.com">info@techua.com</a></p>
    </address>

    <p><small>&copy; 2024 TechUA. Всі права захищені.</small></p>
</footer>

<!-- Footer статті — підпис до конкретної статті -->
<article>
    <h2>Назва статті</h2>
    <p>Текст...</p>
    <footer>
        <p>
            Опубліковано у розділі <a href="/category/frontend">Frontend</a>. Ліцензія:
            <a href="https://creativecommons.org/licenses/by/4.0/" rel="license">CC BY 4.0</a>
        </p>
    </footer>
</article>

<main> — основний вміст документа

<main> (main — основний) представляє домінантний вміст документа — унікальний для цієї сторінки, а не спільний для всіх сторінок (шапка, навігація, підвал — не сюди).

  • На сторінці має бути лише один <main> (але їх може бути кілька, якщо решта мають hidden)
  • Implicit ARIA role: main
  • Не може бути нащадком <article>, <aside>, <footer>, <header>, <nav>
<body>
    <header><!-- шапка --></header>
    <nav><!-- навігація --></nav>

    <main id="main-content">
        <!-- Посилання "Перейти до вмісту" — стандартна практика доступності -->
        <!-- <a href="#main-content" class="skip-link">Перейти до основного вмісту</a> -->

        <h1>Каталог курсів з веб-розробки</h1>

        <section>
            <h2>Frontend-курси</h2>
            <!-- ... -->
        </section>

        <section>
            <h2>Backend-курси</h2>
            <!-- ... -->
        </section>
    </main>

    <aside><!-- бічна панель --></aside>
    <footer><!-- підвал --></footer>
</body>
Додавайте id="main-content" та посилання «Перейти до вмісту» (<a href="#main-content" class="skip-link">) на початку сторінки. Це дозволяє користувачам клавіатури та скринрідерів пропустити навігацію при кожному переході.

<nav> (navigation — навігація) представляє розділ документа з посиланнями навігаційного характеру — списки посилань, що ведуть на інші сторінки або частини поточної.

  • Implicit ARIA role: navigation
  • Не кожна група посилань є <nav>. Підвальні посилання «Конфіденційність», «Умови» — можуть іти в <footer> без <nav>, або з <nav> якщо там великий набір посилань.
<!-- Головна навігація — одна на сторінці, label для скринрідера -->
<nav aria-label="Головне меню">
    <ul>
        <li><a href="/" aria-current="page">Головна</a></li>
        <li>
            <a href="/courses">Курси</a>
            <!-- Вкладене підменю теж може бути <nav> або просто <ul> -->
            <ul>
                <li><a href="/courses/html">HTML і CSS</a></li>
                <li><a href="/courses/js">JavaScript</a></li>
                <li><a href="/courses/react">React</a></li>
            </ul>
        </li>
        <li><a href="/blog">Блог</a></li>
        <li><a href="/contacts">Контакти</a></li>
    </ul>
</nav>

<!-- Хлібні крихти — теж навігація -->
<nav aria-label="Навігаційний шлях (breadcrumb)">
    <ol>
        <li><a href="/">Головна</a></li>
        <li><a href="/courses">Курси</a></li>
        <li><a href="/courses/html">HTML і CSS</a></li>
        <li aria-current="page">Семантичні елементи</li>
    </ol>
</nav>

<!-- Пагінація — навігація між сторінками -->
<nav aria-label="Сторінки результатів">
    <a href="/blog?page=1" aria-label="Перша сторінка">«</a>
    <a href="/blog?page=2" aria-label="Сторінка 2">2</a>
    <a href="/blog?page=3" aria-current="page" aria-label="Поточна: сторінка 3">3</a>
    <a href="/blog?page=4" aria-label="Сторінка 4">4</a>
    <a href="/blog?page=10" aria-label="Остання сторінка">»</a>
</nav>
Якщо на сторінці кілька <nav> — обов'язково додавайте aria-label або aria-labelledby з унікальним описом. Інакше скринрідер оголосить «навігація» кілька разів без контексту.

<article> — незалежний контент

<article> (article — стаття) представляє самодостатню одиницю вмісту, яка може бути незалежно поширена або перевикористана. Наприклад: стаття в газеті, пост у блозі, коментар, картка товару, пост у соцмережі.

  • Implicit ARIA role: article
  • <article> може містити <header>, <footer>, а також інші <article> (вкладені коментарі)

Ключовий тест для <article>: якщо відірвати цей шматок зі сторінки і перемістити в RSS-фід, на іншу сторінку або надіслати листом — він залишиться зрозумілим? Якщо так — це <article>.

<!-- Стаття в блозі -->
<article>
    <header>
        <h2><a href="/blog/semantic-html">Семантика HTML: повний гід</a></h2>
        <p>
            <a href="/authors/maria">Марія Петренко</a> ·
            <time datetime="2024-11-20">20 листопада 2024</time>
        </p>
    </header>

    <p>Семантичний HTML — це не просто тренд...</p>
    <p>В цій статті ми розглянемо...</p>

    <!-- Секції всередині статті -->
    <section>
        <h3>Чому семантика важлива</h3>
        <p>...</p>
    </section>

    <section>
        <h3>Основні семантичні елементи</h3>
        <p>...</p>
    </section>

    <footer>
        <p>Теги: <a href="/tag/html">HTML</a>, <a href="/tag/accessibility">Доступність</a></p>
    </footer>
</article>

<!-- Картка товару — теж article -->
<article aria-label="Ноутбук Apple MacBook Pro 14">
    <figure>
        <img
            src="/images/macbook-pro-14.jpg"
            alt="MacBook Pro 14, срібний колір, вид спереду"
            width="400"
            height="300"
            loading="lazy"
        />
    </figure>
    <h3><a href="/products/macbook-pro-14">MacBook Pro 14" M3 Pro</a></h3>
    <p>Процесор M3 Pro, 18 ГБ RAM, 512 ГБ SSD</p>
    <p><strong>89 999 грн</strong></p>
    <button type="button">Додати до кошика</button>
</article>

<!-- Коментарі — вкладені article всередині статті -->
<article>
    <h2>Стаття</h2>
    <p>Текст статті...</p>

    <section aria-labelledby="comments-heading">
        <h3 id="comments-heading">Коментарі (3)</h3>

        <article>
            <header>
                <p><strong>Олег Мельник</strong> · <time datetime="2024-11-21T09:15">21 листопада, 09:15</time></p>
            </header>
            <p>Чудова стаття! Особливо пункт про aria-label.</p>
        </article>

        <article>
            <header>
                <p><strong>Тетяна Лисенко</strong> · <time datetime="2024-11-21T11:30">21 листопада, 11:30</time></p>
            </header>
            <p>А чи є різниця між section і article в конкретному прикладі?</p>
        </article>
    </section>
</article>

<section> — тематичний розділ

<section> (section — розділ) представляє загальний тематичний розділ документа, зазвичай з заголовком. Це не «обгортка для стилів» — <section> несе семантику: «це окрема тематична одиниця».

  • Implicit ARIA role: region (якщо є доступна назва), generic (якщо немає aria-label/aria-labelledby)
  • Зазвичай <section> починається з заголовка <h2><h6>

<article> vs <section> — ключова різниця

Це одне з найпоширеніших питань:

Незалежний: вміст має сенс поза контекстом сторінки. Можна поширити окремо.

<!-- Пост у блозі — незалежний, можна переопублікувати -->
<article>
    <h2>Як налаштувати ESLint у 2024</h2>
    <p>Покрокова інструкція...</p>
</article>

<!-- Коментар — незалежна думка конкретної людини -->
<article>
    <p><strong>Степан</strong>: Дякую, дуже корисно!</p>
</article>

<!-- Картка товару — незалежна одиниця каталогу -->
<article>
    <h3>Samsung Galaxy S24</h3>
    <p>від 35 000 грн</p>
</article>

<aside> — додатковий контент

<aside> (aside — убік) представляє вміст, що лише опосередковано пов'язаний з навколишнім контентом. Думайте про це як про виносний блок книги, бічну панель або рекламу.

  • Implicit ARIA role: complementary
  • Всередині <article><aside> стосується лише цієї статті. На рівні сторінки — стосується всього сайту.
<!-- Aside на рівні сторінки (бічна панель) -->
<body>
    <main>
        <article>
            <h2>Що таке TypeScript</h2>
            <p>TypeScript — це надмножина JavaScript...</p>
        </article>
    </main>

    <aside aria-label="Пов'язані матеріали">
        <section>
            <h3>Схожі статті</h3>
            <ul>
                <li><a href="/js-basics">Основи JavaScript</a></li>
                <li><a href="/ts-tutorial">TypeScript Tutorial</a></li>
            </ul>
        </section>

        <section>
            <h3>Популярні курси</h3>
            <ul>
                <li><a href="/courses/ts">TypeScript Pro</a> — 2 400 грн</li>
            </ul>
        </section>
    </aside>
</body>

<!-- Aside всередині article — пов'язаний з конкретною статтею -->
<article>
    <h2>Як працює the Event Loop у JavaScript</h2>

    <p>JavaScript є однопотоковою мовою...</p>

    <aside>
        <h4>💡 Що таке стек викликів</h4>
        <p>Call Stack — структура даних LIFO, яка відстежує...</p>
    </aside>

    <p>Завдяки event loop, JavaScript може...</p>
</article>

<address> — контактна інформація

<address> (address — адреса) представляє контактну інформацію для найближчого <article> або <body>. Не лише поштову адресу — будь-який спосіб зв'язатися.

  • Implicit ARIA role: group
  • Не рекомендується вставляти всередину заголовки, <article>, <section>, <header>, <footer>, другий <address>
<!-- Address для автора статті -->
<article>
    <h2>Аналіз продуктивності React 18</h2>
    <p>Вміст статті...</p>
    <footer>
        <address>
            Автор: <a href="mailto:iv.koval@example.com">Іван Коваль</a>,
            <a href="https://twitter.com/ivan_koval" rel="author external" target="_blank">@ivan_koval</a>
        </address>
    </footer>
</article>

<!-- Address для всієї організації в footer -->
<footer>
    <address>
        <p><strong>ТОВ «TechUA»</strong></p>
        <p>01001, Україна, Київ, вул. Хрещатик, 1, офіс 305</p>
        <p>Телефон: <a href="tel:+380441234567">+38 (044) 123-45-67</a></p>
        <p>Email: <a href="mailto:info@techua.com">info@techua.com</a></p>
    </address>
</footer>

<time> — дата і час

<time> (time — час) представляє конкретний момент часу або проміжок часу. Атрибут datetime містить машиночитабельне значення у форматі ISO 8601.

<!-- Дата публікації -->
<time datetime="2024-11-20">20 листопада 2024 року</time>

<!-- Дата + час з часовим поясом -->
<time datetime="2024-11-20T14:30:00+02:00"> 20 листопада 2024, 14:30 за Києвом </time>

<!-- Тривалість (duration) — починається з P -->
<time datetime="PT2H30M">2 години 30 хвилин</time>
<time datetime="P3D">3 дні</time>

<!-- Рік і місяць -->
<time datetime="2024-11">листопад 2024</time>

<!-- Лише рік -->
<time datetime="2024">цього року</time>

<!-- Загальне використання в публікаціях -->
<article>
    <header>
        <h2>Анонс конференції JSFrontend</h2>
        <p>
            Опubліковано:
            <time datetime="2024-10-01T09:00+03:00" pubdate> 1 жовтня 2024 </time>
        </p>
        <p>
            Захід відбудеться:
            <time datetime="2024-12-07">7 грудня 2024</time>, тривалість: <time datetime="PT8H">8 годин</time>.
        </p>
    </header>
</article>
<time> без атрибута datetime є валідним — браузер вважає, що текст всередині є корректним форматом часу. Але завжди краще вказати datetime явно для машинного читання.

<details> та <summary> — розкривний блок

<details> (details — деталі) представляє розкривний блок, вміст якого відображається лише при взаємодії. <summary> (summary — підсумок) — перший дочірній елемент, що стає заголовком-кнопкою.

  • <details> без <summary> — браузер сам генерує мітку (зазвичай «Деталі»)
  • Атрибут open — блок відкритий за замовчуванням
<!-- Базовий приклад -->
<details>
    <summary>Що таке семантичний HTML?</summary>
    <p>
        Семантичний HTML — це підхід до написання коду, при якому кожен тег відображає природу вмісту, а не лише його
        зовнішній вигляд.
    </p>
</details>

<!-- Відкритий за замовчуванням -->
<details open>
    <summary>Системні вимоги (розгорнуто)</summary>
    <ul>
        <li>Node.js 20+</li>
        <li>npm 10+</li>
        <li>Git 2.40+</li>
    </ul>
</details>

<!-- FAQ -->
<section aria-labelledby="faq-title">
    <h2 id="faq-title">Часті запитання</h2>

    <details>
        <summary>Скільки часу займає курс?</summary>
        <p>Курс розрахований на 40 академічних годин &mdash; приблизно 5 тижнів по 8 годин на тиждень.</p>
    </details>

    <details>
        <summary>Чи потрібні попередні знання?</summary>
        <p>Базові знання HTML і CSS будуть корисними, але не обов'язковими.</p>
    </details>
</section>
details {
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    padding: 0.75rem 1rem;
    margin-bottom: 0.75rem;
}
summary {
    cursor: pointer;
    font-weight: 600;
    user-select: none;
    color: #1e293b;
}
details[open] summary {
    color: #3b82f6;
    margin-bottom: 0.5rem;
}
details p, details ul {
    margin: 0.5rem 0 0;
    color: #475569;
}
h2 {
    font-size: 1.125rem;
    margin-bottom: 0.5rem;
    color: #0f172a;
}
Preview

<dialog> — модальне вікно

<dialog> (dialog — діалог) представляє діалогове вікно або інтерактивний підзапит — модальне або немодальне.

  • Атрибут open — вікно відкрите
  • Метод .showModal() — відкриває як модальне (блокує решту сторінки, додає backdrop)
  • Метод .show() — відкриває як немодальне
  • <form method="dialog"> — форма всередині <dialog>, кнопка submit закриває діалог
<!-- HTML-структура -->
<button type="button" id="open-dialog">Відкрити діалог</button>

<dialog id="confirm-dialog" aria-labelledby="dialog-title">
    <h2 id="dialog-title">Підтвердіть дію</h2>
    <p>Ви впевнені, що хочете видалити акаунт? Цю дію не можна скасувати.</p>

    <form method="dialog">
        <button type="submit" value="confirm">Так, видалити</button>
        <button type="submit" value="cancel" autofocus>Скасувати</button>
    </form>
</dialog>

<script>
    const dialog = document.getElementById('confirm-dialog')
    document.getElementById('open-dialog').addEventListener('click', () => {
        dialog.showModal()
    })
    dialog.addEventListener('close', () => {
        console.log('Результат:', dialog.returnValue) // "confirm" або "cancel"
    })
</script>
#open-dialog {
    padding: 0.5rem 1.25rem;
    background: #3b82f6;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 1rem;
}
#open-dialog:hover {
    background: #2563eb;
}
dialog {
    border: none;
    border-radius: 12px;
    padding: 2rem;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
    max-width: 400px;
}
dialog::backdrop {
    background: rgba(0, 0, 0, 0.5);
}
dialog h2 {
    margin: 0 0 0.75rem;
    font-size: 1.25rem;
    color: #111827;
}
dialog p {
    margin: 0 0 1.5rem;
    color: #6b7280;
}
dialog form {
    display: flex;
    gap: 0.75rem;
    justify-content: flex-end;
}
dialog button[value='confirm'] {
    background: #ef4444;
    color: white;
    border: none;
    padding: 0.5rem 1rem;
    border-radius: 6px;
    cursor: pointer;
}
dialog button[value='cancel'] {
    background: #f3f4f6;
    border: 1px solid #d1d5db;
    padding: 0.5rem 1rem;
    border-radius: 6px;
    cursor: pointer;
}
Preview
<dialog> з .showModal() автоматично керує фокусом (пастка фокусу — Tab не виходить за межі), блокує скролінг та додає backdrop. Це нативна браузерна реалізація accessibility pattern «Modal Dialog» з ARIA APG.

<hgroup> — група заголовків

<hgroup> (heading group — група заголовків) представляє основний заголовок разом з підзаголовком. Використовується для семантичного групування заголовка і підзаголовка, щоб підзаголовок не брав участі у побудові outline документа.

<hgroup>
    <h1>TechUA Academy</h1>
    <p>Навчання веб-розробці для тих, хто поважає своє майбутнє</p>
</hgroup>

<hgroup>
    <h2>Курс «React Pro»</h2>
    <p>Від основ компонентів до складних патернів управління станом</p>
</hgroup>

<mark> — виділений текст

<mark> (mark — позначення) представляє текст, виділений для довідки або у зв'язку з інтересами читача — наприклад, підсвічений текст у результатах пошуку.

<!-- Пошуковий результат — підсвічуємо збіги -->
<p>Знайдено результати для запиту <strong>«семантичний HTML»</strong>:</p>
<p>
    <mark>Семантичний HTML</mark> — це підхід до розробки, при якому теги несуть не лише візуальне значення, але й
    <mark>семантичне</mark>.
</p>

<!-- Важлива частина цитати -->
<blockquote>
    «Структура документа повинна відображати його <mark>змістовну ієрархію</mark>, а не лише візуальне розташування
    елементів.»
</blockquote>

<figure> та <figcaption> — самодостатній вміст

Вже розглянуто у файлі 03, але важливо підкреслити семантичне значення. <figure> може містити не лише зображення:

<!-- Зображення з підписом -->
<figure>
    <img
        src="/charts/growth-2024.png"
        alt="Графік зростання: з 10K до 150K користувачів за 2024 рік"
        width="800"
        height="400"
    />
    <figcaption>Рис. 3. Динаміка зростання аудиторії TechUA Academy у 2024 році.</figcaption>
</figure>

<!-- Програмний код -->
<figure>
    <figcaption>Лістинг 1: Компонент авторизації (React + TypeScript)</figcaption>
    <pre><code>interface AuthProps {
  onSuccess: (user: User) => void;
}

const LoginForm: React.FC&lt;AuthProps&gt; = ({ onSuccess }) => {
  // ...
};</code></pre>
</figure>

<!-- Поезія або вірш -->
<figure>
    <blockquote>
        <p>
            Та не однаково мені,<br />
            Як Україну злії люди<br />
            Присплять, лукаві, і в огні<br />
            Її, окраденую, збудять...
        </p>
    </blockquote>
    <figcaption>— Тарас Шевченко, «І мертвим, і живим...», 1845</figcaption>
</figure>

<!-- Таблиця як figure -->
<figure>
    <figcaption>Таблиця 1. Порівняння фреймворків за швидкістю рендерингу</figcaption>
    <table>
        <thead>
            <tr>
                <th scope="col">Фреймворк</th>
                <th scope="col">First Paint (ms)</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <th scope="row">Solid.js</th>
                <td>12</td>
            </tr>
            <tr>
                <th scope="row">Svelte</th>
                <td>18</td>
            </tr>
            <tr>
                <th scope="row">Vue 3</th>
                <td>24</td>
            </tr>
            <tr>
                <th scope="row">React 18</th>
                <td>31</td>
            </tr>
        </tbody>
    </table>
</figure>

Semantic outline: як браузери будують структуру

Семантичні елементи впливають на document outline — ієрархічну структуру документа:

<!-- Заголовки без семантичних елементів — один плоский outline -->
<h1>TechUA Academy</h1>
<h2>Курси</h2>
<h2>Блог</h2>
<h2>Про нас</h2>

<!-- З семантичними елементами — кожна секція має власний outline -->
<body>
    <main>
        <h1>TechUA Academy</h1>
        <!-- Заголовок всього документа -->

        <section>
            <h2>Наші курси</h2>
            <!-- Заголовок розділу -->
            <article>
                <h3>React Pro</h3>
                <!-- Заголовок статті/картки -->
            </article>
        </section>

        <section>
            <h2>Останні публікації</h2>
            <article>
                <h3>Semantic HTML: повний гід</h3>
                <!-- h4 тут — підрозділ всередині article -->
                <section>
                    <h4>Чому семантика важлива</h4>
                </section>
            </article>
        </section>
    </main>

    <aside>
        <h2>Пов'язані ресурси</h2>
        <!-- h2 в aside — окремий контекст -->
    </aside>
</body>

Практичні завдання


Комплексний приклад від А до Я

Тема: Онлайн-видання «DevТижневик» — головна сторінка із статтями, навігацією та додатковим контентом.

<!DOCTYPE html>
<html lang="uk">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="description"
    content="DevТижневик — щотижневий дайджест новин веб-розробки, оглядів технологій і туторіалів.">
  <title>DevТижневик — Головна</title>
  <link rel="canonical" href="https://devweekly.ua/">
</head>
<body>

  <!-- ===== SKIP LINK — Доступність ===== -->
  <a href="#main-content" class="skip-link">Перейти до основного вмісту</a>

  <!-- ===== HEADER ===== -->
  <header>
    <a href="/">
      <img src="/logo.svg" alt="DevТижневик — на головну" width="200" height="55">
    </a>

    <!-- Головна навігація -->
    <nav aria-label="Основне меню">
      <ul>
        <li><a href="/" aria-current="page">Головна</a></li>
        <li><a href="/articles">Статті</a></li>
        <li><a href="/tutorials">Туторіали</a></li>
        <li>
          <a href="/topics">Теми</a>
          <ul>
            <li><a href="/topics/html-css">HTML і CSS</a></li>
            <li><a href="/topics/javascript">JavaScript</a></li>
            <li><a href="/topics/tools">Інструменти</a></li>
          </ul>
        </li>
        <li><a href="/newsletter">Розсилка</a></li>
      </ul>
    </nav>

    <!-- Пошук -->
    <form role="search" action="/search" method="get">
      <label for="site-search">Пошук по виданню</label>
      <input type="search" id="site-search" name="q" placeholder="CSS Grid, React hooks...">
      <button type="submit">Знайти</button>
    </form>
  </header>

  <!-- ===== MAIN ===== -->
  <main id="main-content">

    <!-- Хлібні крихти -->
    <nav aria-label="Розташування на сайті">
      <ol>
        <li><a href="/">Головна</a></li>
        <li aria-current="page">Випуск #148</li>
      </ol>
    </nav>

    <!-- Заголовок сторінки -->
    <hgroup>
      <h1>Випуск #148</h1>
      <p>Найцікавіше у світі веб-розробки за тиждень <time datetime="2024-11-18/2024-11-24">18–24 листопада 2024</time></p>
    </hgroup>

    <!-- ===== ГОЛОВНА СТАТТЯ ===== -->
    <section aria-labelledby="featured-heading">
      <h2 id="featured-heading">Тема тижня</h2>

      <article>
        <header>
          <h3>
            <a href="/articles/css-anchor-positioning">
              CSS Anchor Positioning: кінець JavaScript-позиціонування тултіпів
            </a>
          </h3>
          <p>
            <address>
              <a href="/authors/olena-koval" rel="author">Олена Коваль</a>
            </address>
            ·
            <time datetime="2024-11-20">20 листопада 2024</time>
            · <span>8 хвилин читання</span>
          </p>
        </header>

        <figure>
          <picture>
            <source srcset="/images/css-anchor-mobile.avif" type="image/avif" media="(max-width: 767px)">
            <source srcset="/images/css-anchor-mobile.webp" type="image/webp" media="(max-width: 767px)">
            <source srcset="/images/css-anchor.avif" type="image/avif">
            <source srcset="/images/css-anchor.webp" type="image/webp">
            <img
              src="/images/css-anchor.jpg"
              alt="Схема CSS Anchor Positioning: елемент-якір і прив'язаний тултіп"
              width="800"
              height="450"
              loading="eager"
              fetchpriority="high"
            >
          </picture>
          <figcaption>Рис. 1. Механізм прив'язки CSS Anchor Positioning. Стрілки показують напрямок позиціонування від anchor-point.</figcaption>
        </figure>

        <p>
          Специфікація <mark>CSS Anchor Positioning</mark> нарешті отримала широку підтримку браузерів.
          Тепер розробники можуть позиціонувати елементи відносно будь-якого іншого
          елемента-«якоря» без жодного JavaScript.
        </p>

        <section aria-labelledby="anchor-what-heading">
          <h4 id="anchor-what-heading">Що це означає на практиці</h4>
          <p>Тултіпи, дропдауни, попапи, підказки — всі ці компоненти традиційно потребували...</p>
        </section>

        <aside>
          <h4>📊 Підтримка браузерів на листопад 2024</h4>
          <dl>
            <dt>Chrome / Edge</dt>
            <dd>125+ — повна підтримка</dd>
            <dt>Firefox</dt>
            <dd>133+ — повна підтримка</dd>
            <dt>Safari</dt>
            <dd>Технічний preview</dd>
          </dl>
        </aside>

        <details>
          <summary>Дивитися специфікацію та ресурси</summary>
          <ul>
            <li><a href="https://drafts.csswg.org/css-anchor-position-1/" rel="external noopener" target="_blank">CSS Anchor Positioning Level 1 Draft</a></li>
            <li><a href="https://developer.chrome.com/blog/anchor-positioning-api" rel="external noopener" target="_blank">Chrome Developers: Anchor Positioning API</a></li>
            <li><a href="https://caniuse.com/css-anchor-positioning" rel="external noopener" target="_blank">Can I Use: CSS Anchor Positioning</a></li>
          </ul>
        </details>

        <footer>
          <p>Теги:
            <a href="/topics/css" rel="tag">CSS</a>,
            <a href="/topics/layouts" rel="tag">Верстка</a>,
            <a href="/topics/browser-apis" rel="tag">Browser APIs</a>
          </p>
        </footer>
      </article>
    </section>

    <!-- ===== ДАЙДЖЕСТ НОВИН ===== -->
    <section aria-labelledby="digest-heading">
      <h2 id="digest-heading">Новини тижня</h2>

      <article>
        <h3><a href="/news/node-23">Node.js 23 вийшов із підтримкою require(ESM)</a></h3>
        <p>
          <time datetime="2024-11-19">19 листопада</time>          Довгоочікувана функція дозволяє імпортувати ES-модулі через CommonJS require...
        </p>
      </article>

      <article>
        <h3><a href="/news/vite-6">Vite 6.0: середовищна модель та Rolldown</a></h3>
        <p>
          <time datetime="2024-11-21">21 листопада</time>          Мажорна версія вводить Environment API для кращого контролю серверного та клієнтського бандлу...
        </p>
      </article>

      <article>
        <h3><a href="/news/tailwind-4">Tailwind CSS 4.0 beta: новий CSS-перший підхід</a></h3>
        <p>
          <time datetime="2024-11-22">22 листопада</time>          Конфігурація тепер на CSS-змінних замість JavaScript-файлу...
        </p>
      </article>
    </section>

    <!-- ===== ДІАЛОГ підписки ===== -->
    <button type="button" id="subscribe-btn">📧 Підписатися на розсилку</button>

    <dialog id="subscribe-dialog" aria-labelledby="dialog-heading">
      <h2 id="dialog-heading">Підписки на DevТижневик</h2>
      <p>Отримуйте найкраще з веб-розробки щопонеділка — у вашій поштовій скриньці.</p>
      <form method="dialog" action="/subscribe">
        <label for="dialog-email">Email</label>
        <input type="email" id="dialog-email" name="email" required autocomplete="email" placeholder="ваш@email.com">
        <button type="submit" value="subscribe">Підписатися</button>
        <button type="submit" value="cancel" autofocus>Не зараз</button>
      </form>
    </dialog>

  </main>

  <!-- ===== ASIDE ===== -->
  <aside aria-label="Додаткові матеріали">

    <section>
      <h2>Популярні теми</h2>
      <ul>
        <li><a href="/topics/javascript">JavaScript</a></li>
        <li><a href="/topics/typescript">TypeScript</a></li>
        <li><a href="/topics/react">React</a></li>
        <li><a href="/topics/performance">Продуктивність</a></li>
      </ul>
    </section>

    <section>
      <h2>Інструмент тижня</h2>
      <article>
        <h3><a href="https://biome.js.org" rel="external noopener" target="_blank">Biome 1.9</a></h3>
        <p>Швидкий linter + formatter для JS/TS на Rust. Замінює ESLint + Prettier.</p>
        <dl>
          <dt>Ліцензія</dt>
          <dd>MIT</dd>
          <dt>GitHub зірки</dt>
          <dd>17K+</dd>
        </dl>
      </article>
    </section>

  </aside>

  <!-- ===== FOOTER ===== -->
  <footer>
    <nav aria-label="Навігація підвалу">
      <ul>
        <li><a href="/about">Про видання</a></li>
        <li><a href="/advertise">Реклама</a></li>
        <li><a href="/rss.xml" type="application/rss+xml">RSS</a></li>
        <li><a href="/privacy">Конфіденційність</a></li>
        <li><a href="/sitemap.xml">Карта сайту</a></li>
      </ul>
    </nav>

    <address>
      Редакція DevТижневик:
      <a href="mailto:editors@devweekly.ua">editors@devweekly.ua</a>
    </address>

    <p>
      <small>
        &copy; <time datetime="2024">2024</time> DevТижневик.
        Матеріали поширюються за ліцензією
        <a href="https://creativecommons.org/licenses/by/4.0/" rel="license" target="_blank">CC BY 4.0</a>.
      </small>
    </p>
  </footer>

  <script>
    // Підписка через dialog
    const btn = document.getElementById('subscribe-btn');
    const dialog = document.getElementById('subscribe-dialog');
    btn.addEventListener('click', () => dialog.showModal());
    dialog.addEventListener('close', () => {
      if (dialog.returnValue === 'subscribe') {
        console.log('Підписано!');
      }
    });
  </script>

</body>
</html>

Що демонструє цей приклад:

🏗️ Повна структура

<header>, <main>, <aside>, <footer> на рівні документа. Всередині — <section>, <article> з власними <header>/<footer>. Правильний document outline.

🧭 Навігація

Три різні <nav> з унікальними aria-label: головне меню, хлібні крихти, підвальна навігація. aria-current="page".

📝 Статті та вміст

<article> для кожної новини та головної статті. <figure>/<figcaption> для зображення та таблиці. <aside> всередині article. <details>/<summary> для ресурсів.

⏱️ Час та автори

<time datetime> для всіх дат. <address> для авторів у правильному контексті. <hgroup> для заголовка з підзаголовком. <mark> для виділення.

📬 Діалог

<dialog> з .showModal(), форма з method="dialog", aria-labelledby, autofocus на кнопці скасування.

🌐 Доступність

Skip link, aria-label для <nav>, aria-current, aria-labelledby для <section>. rel="author", rel="tag", rel="license".

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

Copyright © 2026