Jeśli używałeś <div> do wszystkiego — opakowania strony, nawigacji, kontenera artykułu, paska bocznego — nie jesteś sam. To był domyślny ruch przez lata. Ale HTML ma właściwe elementy semantyczne od czasów HTML5 i ich używanie sprawia, że kod jest łatwiejszy do czytania, lepszy dla SEO i naprawdę bardziej dostępny. Przyjrzyjmy się elementom, które naprawdę mają znaczenie.

Słowo „semantyczny" oznacza po prostu, że element niesie ze sobą znaczenie wykraczające poza jego wizualną prezentację. <div> nic nie oznacza — to ogólny blok. <nav> mówi przeglądarce, wyszukiwarkom i technologiom wspomagającym: „tutaj są linki nawigacyjne". Ten kontekst nic cię nie kosztuje, a przynosi wielorakie korzyści.

Dlaczego Semantyka Ma Znaczenie

Trzy realne korzyści, nie teoretyczne:

  • SEO. Wyszukiwarki używają struktury dokumentu do rozumienia hierarchii treści. Element <main> sygnalizuje treść główną. <article> wewnątrz niego sygnalizuje samodzielną całość. Google używa tych sygnałów przy rankingowaniu stron.
  • Dostępność. Czytniki ekranu udostępniają użytkownikom regiony orientacyjne. Dzięki <nav>, <main> i <aside> na miejscu, użytkownicy klawiatury mogą przeskakiwać bezpośrednio do sekcji, której szukają — bez tabulowania przez każdy link na stronie.
  • Łatwość utrzymania. Nowy programista czytający twój szablon od razu rozumie strukturę strony. <header>/<main>/<footer> samo się dokumentuje w sposób, w jaki <div class="top">/<div class="content">/<div class="bottom"> nie jest w stanie.

Podstawowe Elementy Układu

Te pięć elementów obsługuje zewnętrzny szkielet większości stron. Używaj ich na każdej stronie, a już wygrałeś połowę semantycznej bitwy:

html
<body>
  <header>
    <a href="/" class="logo">MyBlog</a>
    <nav>
      <ul>
        <li><a href="/articles">Articles</a></li>
        <li><a href="/about">About</a></li>
        <li><a href="/contact">Contact</a></li>
      </ul>
    </nav>
  </header>

  <main>
    <!-- Primary page content goes here -->
  </main>

  <aside>
    <!-- Sidebar: related links, ads, author bio -->
  </aside>

  <footer>
    <p>&copy; 2026 MyBlog. All rights reserved.</p>
  </footer>
</body>
  • <header>. Treść wprowadzająca dla strony lub sekcji. Zazwyczaj zawiera logo, tytuł strony i główną nawigację. Możesz go również używać wewnątrz <article> dla własnego bloku nagłówka artykułu.
  • <nav>. Zestaw linków nawigacyjnych. Nie każda grupa linków potrzebuje <nav> — używaj go dla głównych bloków nawigacyjnych, takich jak nawigacja po stronie lub paginacja.
  • <main>. Dominująca treść strony. Na każdej stronie powinien być tylko jeden <main> i nie powinien zawierać niczego powtarzającego się na stronach (nagłówek, nawigacja, stopka).
  • <aside>. Treść luźno powiązana z treścią główną. Paski boczne, cytaty wyróżnione, powiązane artykuły. Czytniki ekranu udostępniają to jako punkt orientacyjny uzupełniający.
  • <footer>. Stopka strony lub sekcji. Może zawierać informacje o prawach autorskich, nawigację pomocniczą, linki kontaktowe lub krótką notatkę o autorze.
Częsty błąd: <header> i <footer> nie są ograniczone do poziomu strony. Każdy <article> lub <section> może mieć własny <header> i <footer>. Jest to szczególnie przydatne dla podpisów wpisów na blogu i metadanych na poziomie artykułu.

article vs section — Kiedy Używać Każdego

To jest element, który sprawia ludziom najwięcej trudności. Reguła kciuka, która faktycznie działa: jeśli treść miałaby sens, gdybyś ją syndykował samodzielnie (np. opublikował jako wpis RSS lub udostępnił na innej stronie), to jest to <article>. Jeśli ma sens tylko jako część większej całości, to jest to <section>.

html
<!-- article: self-contained, could stand alone -->
<article>
  <header>
    <h2>Understanding HTTP/2 Push</h2>
    <p>By Alice Chen — <time datetime="2026-03-15">March 15, 2026</time></p>
  </header>
  <p>HTTP/2 Server Push lets the server send resources...</p>
  <footer>
    <p>Tagged: <a href="/tags/http">HTTP</a>, <a href="/tags/performance">Performance</a></p>
  </footer>
</article>

<!-- section: thematic grouping within a page -->
<section>
  <h2>Related Articles</h2>
  <ul>
    <li><a href="/http3-quic">HTTP/3 and QUIC Explained</a></li>
    <li><a href="/cdn-strategy">CDN Strategy for Global Apps</a></li>
  </ul>
</section>

<section> powinien zawsze mieć nagłówek (<h2> do <h6>). Jeśli twoja sekcja nie ma nagłówka, to zazwyczaj znak, że powinieneś użyć <div>. Specyfikacja WHATWG jest jasna: <section> służy do tematycznych grupowań, a nie do dowolnych podziałów układu.

figure, figcaption, time i address

Cztery elementy, które programiści często pomijają, a regularnie ich potrzebują:

html
<!-- figure + figcaption: images, code blocks, diagrams, charts -->
<figure>
  <img src="/images/latency-chart.png" alt="P99 latency over 24 hours showing a spike at 14:30">
  <figcaption>Figure 1: API latency during the Tuesday incident (UTC).</figcaption>
</figure>

<!-- A code block as a figure (totally valid) -->
<figure>
  <pre><code>const result = await db.query('SELECT * FROM users WHERE active = true');</code></pre>
  <figcaption>Listing 1: Basic active user query.</figcaption>
</figure>

<!-- time: machine-readable dates and times -->
<p>Published <time datetime="2026-04-16T09:00:00Z">April 16, 2026</time></p>
<p>Sale ends in <time datetime="PT2H30M">2 hours 30 minutes</time></p>

<!-- address: contact info for nearest article or body ancestor -->
<address>
  Written by <a href="mailto:[email protected]">Alice Chen</a>.<br>
  123 Dev Street, San Francisco, CA.
</address>
  • <figure>. Dowolna samodzielna treść, do której odwołuje się główny przepływ — obrazy, listingi kodu, wykresy, filmy. Kluczowe: można by ją przenieść do dodatku bez zakłócania toku czytania.
  • <figcaption>. Podpis dla <figure>. Musi być pierwszym lub ostatnim potomkiem <figure>. Zapewnia semantyczne powiązanie między podpisem a treścią, czego <p> pod obrazem nie robi.
  • <time>. Daty czytelne dla człowieka z atrybem datetime czytelnym dla maszyny. Wartość datetime używa formatu ISO 8601. Wyszukiwarki używają tego do oznaczenia wydarzeń i sygnałów świeżości.
  • <address>. Dane kontaktowe dla najbliższego przodka <article> (lub całego <body>). Nie jest to element adresu ogólnego przeznaczenia — jest przeznaczony konkretnie dla danych kontaktowych autora treści lub właściciela strony.

Antywzorzec Divitis

„Divitis" to coś, co dzieje się, gdy każdy element w twoim HTML jest <div>. Oto realny przykład przed i po, który pokazuje problem:

html
<!-- Before: divitis -->
<div class="page">
  <div class="header">
    <div class="brand">TechBlog</div>
    <div class="nav">
      <div class="nav-item"><a href="/posts">Posts</a></div>
    </div>
  </div>
  <div class="post">
    <div class="post-title">Why TypeScript Is Worth It</div>
    <div class="post-meta">By Bob — Jan 2026</div>
    <div class="post-body">TypeScript adds static types to JavaScript...</div>
  </div>
</div>

<!-- After: semantic HTML -->
<body>
  <header>
    <span class="brand">TechBlog</span>
    <nav>
      <a href="/posts">Posts</a>
    </nav>
  </header>
  <main>
    <article>
      <header>
        <h1>Why TypeScript Is Worth It</h1>
        <p>By Bob — <time datetime="2026-01-10">January 10, 2026</time></p>
      </header>
      <p>TypeScript adds static types to JavaScript...</p>
    </article>
  </main>
</body>

Wersja semantyczna jest faktycznie krótsza, wymaga mniej nazw klas CSS i daje przeglądarkom oraz narzędziom wspomagającym wszystko, czego potrzebują do zrozumienia struktury strony. Nadal możesz stylizować wszystko dokładnie tak samo za pomocą CSS — elementy semantyczne nie narzucają żadnych ograniczeń wizualnych.

Role ARIA — Ostateczność, Nie Pierwszy Krok

Role ARIA pozwalają dodawać znaczenie semantyczne do elementów, które go natywnie nie mają. Problem w tym, że programiści często sięgają po ARIA, gdy natywny element HTML wykonałby pracę lepiej. Pierwsza zasada ARIA: nie używaj ARIA, jeśli możesz użyć natywnego HTML.

html
<!-- Wrong: using ARIA where native HTML works -->
<div role="navigation">
  <a href="/home">Home</a>
</div>

<!-- Right: just use the semantic element -->
<nav>
  <a href="/home">Home</a>
</nav>

<!-- ARIA is appropriate here: custom widget with no HTML equivalent -->
<div
  role="tablist"
  aria-label="Code examples"
>
  <button role="tab" aria-selected="true" aria-controls="panel-js">JavaScript</button>
  <button role="tab" aria-selected="false" aria-controls="panel-py">Python</button>
</div>

Używaj ARIA dla interaktywnych widżetów, których natywny HTML nie obsługuje — panele z zakładkami, pola kombi, widoki drzew, wybieraki dat. W przypadku strukturalnych punktów orientacyjnych, takich jak nawigacja, nagłówki i treść główna, zawsze polegaj na natywnych elementach.

Kompletna Struktura Strony Wpisu Blogowego

Tak wygląda w pełni semantyczna strona wpisu blogowego. To wzorzec stosowany na stronach, które dobrze wypadają w audytach dostępności bez dodatkowego wysiłku:

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Why TypeScript Is Worth It — TechBlog</title>
</head>
<body>
  <header>
    <a href="/" class="logo">TechBlog</a>
    <nav aria-label="Site navigation">
      <ul>
        <li><a href="/posts">Posts</a></li>
        <li><a href="/topics">Topics</a></li>
        <li><a href="/about">About</a></li>
      </ul>
    </nav>
  </header>

  <main>
    <article>
      <header>
        <h1>Why TypeScript Is Worth It</h1>
        <address>
          By <a rel="author" href="/authors/bob">Bob Martinez</a>
        </address>
        <p>Published <time datetime="2026-01-10">January 10, 2026</time></p>
      </header>

      <section>
        <h2>The Setup Cost Is Real but Front-Loaded</h2>
        <p>TypeScript adds a compilation step and requires type annotations...</p>

        <figure>
          <pre><code>interface User {
  id: number;
  name: string;
  email: string;
}</code></pre>
          <figcaption>Listing 1: A typed User interface catches shape errors at compile time.</figcaption>
        </figure>
      </section>

      <section>
        <h2>Where TypeScript Pays Off</h2>
        <p>The ROI kicks in as team size and codebase complexity grows...</p>
      </section>

      <footer>
        <p>
          Tagged: <a href="/tags/typescript">TypeScript</a>,
          <a href="/tags/javascript">JavaScript</a>
        </p>
      </footer>
    </article>

    <aside aria-label="Related articles">
      <h2>You Might Also Like</h2>
      <ul>
        <li><a href="/ts-vs-js">TypeScript vs JavaScript — A Practical Comparison</a></li>
        <li><a href="/ts-generics">TypeScript Generics Explained</a></li>
      </ul>
    </aside>
  </main>

  <footer>
    <nav aria-label="Footer navigation">
      <a href="/privacy">Privacy</a>
      <a href="/terms">Terms</a>
    </nav>
    <p><small>&copy; 2026 TechBlog</small></p>
  </footer>
</body>
</html>

Zwróć uwagę na podwójne użycie <header> i <footer> — raz na poziomie strony, raz wewnątrz <article>. To zamierzone i poprawne. Zwróć też uwagę na dwa elementy <nav> z odrębnymi wartościami aria-label — zapobiega to temu, by czytniki ekranu listowały dwa identyczne regiony „nawigacja".

Szybka Pomoc: Przydatne Narzędzia

Jeśli pracujesz z HTML i chcesz wyczyścić lub zwalidować swój kod, Formater HTML ładnie go sformatuje i poprawi, a Walidator HTML wykryje błędy strukturalne, takie jak niezamknięte tagi i brakujące wymagane atrybuty. Dla głębszego audytu dostępności, web.dev/accessibility oferuje doskonałe diagnostyki i wyjaśnienia.

Podsumowanie

Semantyczny HTML nie polega na przestrzeganiu reguł dla samych reguł — chodzi o pisanie kodu, który wyraźnie komunikuje się z przeglądarkami, wyszukiwarkami, narzędziami wspomagającymi i kolejnym programistą czytającym twój szablon. Podstawowe elementy są proste: <header>, <nav>, <main>, <article>, <section>, <aside> i <footer> obejmują zdecydowaną większość rzeczywistych struktur stron. Dodaj <figure>, <time> i <address> tam, gdzie pasują, sięgaj po ARIA tylko wtedy, gdy natywny HTML zawodzi, a będziesz mieć kod, z którym naprawdę przyjemnie się pracuje.