Навигация

Семантический HTML, landmarks и доступность

Семантика — это не про то, чтобы заменить div на section ради красивого слова. Это про контракт между разметкой и вспомогательными технологиями: скринридерами, поисковиками, клавиатурной навигацией и будущими инструментами. Правильная разметка позволяет пользователю понять структуру страницы, не глядя на неё.

Содержание

  1. Глубокий разбор
  2. Практические примеры
  3. Типичные ошибки и антипаттерны
  4. Ключевые тезисы для интервью
  5. Заключение
  6. Полезные ссылки

Глубокий разбор

Что такое семантический HTML

Семантический HTML — это разметка, в которой элементы передают смысл, а не только визуальное оформление. div и span не несут семантической нагрузки: они нужны для группировки и стилизации. <article>, <nav>, <button>, <time> несут контракт: браузер и вспомогательные технологии знают, что это такое.

Ключевое правило: если можно использовать семантический элемент вместо ARIA-роли — используй элемент. <button> лучше <div role="button" tabindex="0">, потому что он уже включает в себя фокус, активацию по Enter/Space, корректную роль и поддержку disabled-состояния.

Landmarks: навигация по структуре страницы

Landmarks — это ориентиры, по которым пользователи скринридеров перемещаются между крупными областями страницы. В ARIA их шесть основных:

  • banner — <header> в контексте страницы;
  • navigation — <nav>;
  • main — <main>;
  • complementary — <aside>;
  • contentinfo — <footer> в контексте страницы;
  • search — явная ARIA-роль search, потому что у HTML нет семантического тега для поиска.
<body>
  <header>
    <a href="/">Логотип</a>
    <nav aria-label="Главное меню">
      <a href="/about">О нас</a>
      <a href="/contacts">Контакты</a>
    </nav>
  </header>

  <main>
    <article>
      <h1>Заголовок статьи</h1>
      <p>Текст статьи...</p>
    </article>
  </main>

  <aside aria-label="Похожие материалы">
    <h2>Читайте также</h2>
  </aside>

  <footer>
    <p>© 2026</p>
  </footer>
</body>

Важно: header и footer дают роли banner/contentinfo только на уровне страницы, то есть когда они являются прямыми потомками body. Внутри <article> или <section> они остаются просто структурными элементами без landmark-ролей.

Default ARIA roles: когда роль уже встроена

Большинство семантических HTML-элементов имеют встроенные роли. Эти роли работают автоматически, и их не нужно дублировать через role:

HTMLDefault role
<button>button
<a href="...">link
<nav>navigation
<main>main
<article>article
<aside>complementary
<h1>–<h6>heading
<ul> / <ol>list
<li>listitem
<table>table

Пример избыточной разметки:

<!-- Плохо: роль дублирует семантику тега -->
<nav role="navigation">...</nav>
<button role="button">Отправить</button>

Исключение — когда нужно изменить или доопределить поведение. Например, <button> превращают в role="tab" для вкладок, но при этом разработчик берёт на себя полную ответственность за keyboard-навигацию и состояния.

Заголовки и структура документа

Скринридеры используют заголовки как оглавление страницы. Иерархия должна быть логичной: h1 — один на страницу (обычно), h2 — разделы, h3 — подразделы. Пропуск уровней (h1 → h3) не ломает валидацию, но путает навигацию.

<!-- Хорошо -->
<h1>Руководство по доступности</h1>
  <h2>Landmarks</h2>
    <h3>Основные ориентиры</h3>
    <h3>Дополнительные ориентиры</h3>
  <h2>Формы</h2>

<!-- Плохо: пропуск уровня и дублирование h1 -->
<h1>Руководство</h1>
  <h3>Landmarks</h3>
  <h1>Формы</h1>

Изображения и альтернативный текст

Атрибут alt решает три задачи:

  1. Описывает содержимое изображения, если оно несёт смысл.
  2. Скрывает декоративное изображение при alt="".
  3. Показывает fallback, если картинка не загрузилась.
<!-- Информативное изображение -->
<img src="chart.png" alt="График роста продаж за 2025 год: +23%">

<!-- Декоративное изображение -->
<img src="divider.svg" alt="">

<!-- Плохо: отсутствие alt заставляет скринридер читать имя файла -->
<img src="IMG_1234.jpg">

Отсутствие alt и пустой alt — разные вещи. Пустой alt говорит: “это декоративное изображение, игнорируй”. Отсутствие alt говорит: “разработчик забыл описать”.

Формы и метки

Каждый интерактивный элемент формы должен иметь доступное имя. Лучший способ — связать <label> с полем через атрибут for:

<label for="email">Email</label>
<input type="email" id="email" name="email" autocomplete="email" required>

Если визуальная метка не нужна, используй aria-label или aria-labelledby:

<input type="search" aria-label="Поиск по сайту">

<span id="search-label">Поиск</span>
<input type="search" aria-labelledby="search-label">

placeholder не заменяет <label>: он пропадает при вводе и часто имеет недостаточный контраст.

Кнопки и ссылки: разделяй ответственность

  • <a href="..."> — навигация: переход на другую страницу или якорь.
  • <button> — действие внутри страницы: отправка формы, открытие модалки, переключение вкладки.
<!-- Правильно -->
<a href="/about">Подробнее о компании</a>
<button type="button" aria-expanded="false">Показать фильтры</button>

<!-- Неправильно: ссылка без href и с действием вместо навигации -->
<a href="#" onclick="openModal()">Открыть</a>

ARIA: добавлять только когда нужно

ARIA не делает элемент интерактивным автоматически. Если навесить role="button" на <div>, нужно самому реализовать:

  • tabindex="0" для фокуса;
  • обработчики Enter и Space;
  • состояния aria-pressed, aria-expanded;
  • визуальный фокус.

Поэтому первый вопрос при использовании ARIA: “нельзя ли заменить это нативным HTML-элементом?”

Когда ARIA нужна:

  • сложные виджеты: вкладки, аккордеоны, комбобоксы, диалоги;
  • динамические уведомления (role="alert", aria-live);
  • связи, которые HTML не выражает напрямую (aria-describedby, aria-controls, aria-owns);
  • исправление недоступных сторонних компонентов.
<!-- Диалог с корректной ролью и фокус-менеджментом -->
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
  <h2 id="dialog-title">Подтвердить действие</h2>
  <p>Вы уверены?</p>
  <button type="button">Отмена</button>
  <button type="button">Удалить</button>
</div>

Focus management

Все интерактивные элементы должны быть доступны с клавиатуры. Проверяйте страницу без мыши: Tab для перехода, Shift+Tab для обратного, Enter/Space для активации.

Антипаттерны:

  • outline: none без замены визуального фокуса;
  • <div onclick="..."> без tabindex и keyboard-обработчиков;
  • модальные окна без фокуса внутри и без возврата фокуса при закрытии.
/* Плохо: пользователь теряет понимание, где находится фокус */
button:focus {
  outline: none;
}

/* Хорошо: замена outline на видимый стиль */
button:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

Практические примеры

Пример 1: карточка статьи

<article>
  <header>
    <h2><a href="/post/1">Как оптимизировать рендеринг</a></h2>
    <p><time datetime="2026-09-03">3 сентября 2026</time></p>
  </header>
  <p>Краткое описание статьи...</p>
  <footer>
    <a href="/tag/performance">#performance</a>
  </footer>
</article>

<article> создаёт самостоятельный фрагмент. <header> и <footer> внутри него не становятся landmarks, но сохраняют структурное значение.

Пример 2: навигация с выпадающим меню

<nav aria-label="Главное меню">
  <ul>
    <li><a href="/">Главная</a></li>
    <li>
      <button type="button" aria-expanded="false" aria-controls="submenu-products">
        Продукты
      </button>
      <ul id="submenu-products" hidden>
        <li><a href="/product-a">Продукт A</a></li>
        <li><a href="/product-b">Продукт B</a></li>
      </ul>
    </li>
  </ul>
</nav>

Кнопка управляет видимостью подменю. aria-expanded сообщает скринридеру, открыто ли меню. aria-controls связывает кнопку и подменю.

Пример 3: статус операции

<div role="status" aria-live="polite" aria-atomic="true">
  Файл успешно загружен
</div>

role="status" — это вежливое уведомление: скринридер произнесёт его, не прерывая текущую речь. Для ошибок используй role="alert" (aria-live="assertive").

Типичные ошибки и антипаттерны

  • <div>-суп вместо семантики. Если элемент — заголовок, список, кнопка, цитата — используй соответствующий тег.
  • Дублирование default ARIA roles. <nav role="navigation"> избыточно.
  • Пропуск alt у информативных изображений. Скринридер начнёт читать URL или имя файла.
  • placeholder вместо <label>. Метка должна быть видна или доступна постоянно.
  • Ссылки, которые делают действия. <a href="#" onclick="..."> ломает навигацию с клавиатуры и ожидания пользователя.
  • <button> внутри <a> или наоборот. Вложенность интерактивных элементов недопустима: скринридер не поймёт, что активировать.
  • Отсутствие фокуса в модалках. При открытии диалога фокус должен переходить внутрь, при закрытии — возвращаться к вызвавшему элементу.
  • Слишком много ARIA. ARIA — это заплатка, а не замена HTML. Сначала используй нативные элементы.

Ключевые тезисы для интервью

  • Семантический HTML важен не сам по себе, а как контракт со скринридерами, поисковиками и клавиатурной навигацией. Нативные элементы (<button>, <a href>) имеют встроенную семантику, keyboard handling и focus management — <div role="button"> требует ручной реализации всего этого.
  • Landmarks — <header>, <nav>, <main>, <aside>, <footer> и role="search" — позволяют пользователям скринридеров быстро перемещаться между смысловыми блоками страницы. <header> и <footer> создают landmarks, только если находятся на верхнем уровне (не вложены в <article> или <section>).
  • У семантических элементов есть default ARIA roles; дублировать их через role не нужно — это избыточно и может запутать вспомогательные технологии.
  • Пустой alt="" скрывает декоративное изображение от скринридеров; отсутствие атрибута alt — ошибка доступности, скринридер зачитает имя файла.
  • Заголовки (h1–h6) образуют оглавление документа для скринридера; пропуск уровней (например, h1 сразу на h3) и несколько h1 путают навигацию.
  • ARIA полезна для сложных виджетов, но не делает <div> полноценной кнопкой автоматически — keyboard handling (Enter, Space), focus management и доступное имя приходится реализовывать вручную. Каждое интерактивное поле формы должно иметь доступное имя через <label>, aria-label или aria-labelledby.

Заключение

Семантический HTML — это контракт: браузер и вспомогательные технологии знают, как работать с <button>, <nav>, <main> без дополнительных инструкций. Landmarks позволяют пользователям скринридеров перепрыгивать между смысловыми блоками страницы. ARIA расширяет семантику для сложных виджетов, но является второй линией обороны — после нативных элементов. Доступность начинается с правильной разметки, продолжается в управлении фокусом и заканчивается тестированием реальными пользователями или скринридерами.

Полезные ссылки