Смысл вместо div: заголовки и ориентиры страницы

разметка≈ 12 минут

Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.

Страницу кофейни можно сверстать из одних <div>, и на экране никто не заметит разницы. Заметит тот, кто страницу не видит: программа чтения с экрана не окидывает её взглядом, а просит у браузера список — где шапка, где меню, где главное содержимое и какие есть заголовки. Этот список браузер собирает из тегов, и теги в нём решают всё. Разница между «страница выглядит правильно» и «страницей можно пользоваться» укладывается в восемь тегов: из них складывается карта, по которой незрячий человек прыгает по странице одной клавишей. Соберём эту карту на кофейне, посмотрим, что браузер делает с выдуманным тегом, сложим из заголовков оглавление и сверимся с настоящей картой, которую строит сам браузер.

Разделы темы:·

Программа чтения с экрана произносит текст страницы вслух или выводит его на брайлевский дисплей. Ею пользуются люди, которые не видят экран или видят его плохо. Слушать страницу подряд, от первой буквы до последней, долго. Поэтому человек сначала узнаёт её устройство: просит список ориентиров — крупных областей вроде шапки, меню и основного содержимого — и прыгает к нужной одной клавишей. В бесплатной программе NVDA для Windows к следующему ориентиру ведёт клавиша D, к следующему заголовку — H.

Откуда программа знает, где шапка? Не по виду: крупный жирный текст наверху для неё ничего не значит. Она читает теги. Проверим на двух элементах с одинаковым текстом — один <div>, другой <header>. Замер спросит у обоих свойство display, то есть как элемент встаёт на странице: block — отдельной полосой во всю ширину, inline — внутри строки, как слово в тексте. Затем попросит список ориентиров. Адрес "div, header" через запятую значит «все div и все header», и ответы идут в порядке разметки.

Проверьте себя до запуска: одинаковый ли display у двух элементов и найдётся ли на странице хотя бы один ориентир? Разбор ответа откроется под примером — он и есть настоящий результат, строка вывода в этой статье спрятана.

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
разбор вывода
Оба элемента — блоки, и в рамке это две одинаковые строки. А ориентир один: banner, шапка сайта. Его дал <header>. <div> — ящик для раскладки без всякого смысла: браузер его рисует, а рассказать о нём программе чтения нечего. Слова вроде banner — это роли, общий словарь, которым браузер пересказывает страницу программам чтения. Словарь описан в стандарте ARIA, и роли в нём английские.

Вид задают стили, смысл — тег. Покрасить под шапку можно что угодно, а программа чтения узнаёт шапку по тегу.

Ориентиры расставляют внутри <body> — видимой части скелета страницы. Карту складывают восемь тегов: <header>, <nav>, <main>, <search>, <aside>, <section>, <article> и <footer>. В таблицу для сравнения добавлены <form> и <div>: первый ориентиром бывает, второй — никогда. Последняя колонка — условие, при котором тег вообще считается ориентиром. Держите таблицу под рукой: задачи в конце статьи решаются по ней, а не по памяти.

ТегЧто внутриОриентир
<header>шапка: название, логотип, иногда менюbanner, если не лежит внутри article, aside, main, nav или section
<nav>ссылки для перехода по сайту или страницеnavigation — всегда
<main>главное содержимое, одно на страницуmain — всегда
<search>поиск или фильтрsearch — всегда
<aside>то, что сбоку от главного: акция, «читайте также»complementary, если не лежит внутри article, aside, nav или section, или если у него есть подпись
<section>раздел со своей темойregion — только с подписью
<article>самостоятельный кусок: пост, отзыв, карточка товаране ориентир, но шапка и подвал внутри него — уже не шапка и подвал сайта
<footer>подвал: контакты, документы, копирайтcontentinfo — по тому же правилу, что <header>
<form>поля ввода и кнопка отправкиform — только с подписью
<div>ящик для раскладкиникогда

Подпись — атрибут aria-label: <section aria-label="Скидки">. Программа чтения произносит её вместе с ролью, а в разборе она показана в ёлочках после роли.

Условия в последней колонке — правило области: роль зависит от того, внутри чего лежит тег. И это не каприз спецификации, а здравый смысл. Шапка внутри <article> — шапка поста, а не сайта, и объявлять её второй шапкой сайта было бы неправдой: человек прыгнул бы к ней в ожидании меню и оказался в середине текста. Разделов <section> на странице бывают десятки, а карта из тридцати безымянных областей ничем не лучше пустой — ориентиром раздел становится, только когда у него есть имя. С <aside> так же: на уровне страницы или внутри <main> это боковая колонка, а внутри раздела — просто врезка в текст.

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

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
разбор вывода
Четыре ориентира, и точка у navigation: меню лежит внутри шапки. <p> и <h1> ориентирами не бывают, поэтому в списке их нет. Эти четыре тега — скелет почти любой страницы, и уже их хватает, чтобы человек одной клавишей перескочил через шапку и меню прямо к главному.

Теперь страница, на которой срабатывают почти все условия таблицы: разделы с подписью и без, статья со своими шапкой и подвалом, две врезки <aside> — одна в <main>, другая внутри раздела, — поиск и две формы. У полей ввода тоже есть aria-label, но поле ориентиром не является: подпись просто даёт ему имя. Прогноз — все строки по порядку, с точками и подписями.

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
разбор вывода
Из списка выпали шестеро: оба раздела без подписи, шапка и подвал статьи, врезка внутри <section> и форма без подписи внутри поиска. Врезка внутри <main> осталась ориентиром и получила точку, как и раздел «Скидки». Подписанная форма «Подписка» и подвал © 2026 стоят на уровне страницы, поэтому точек у них нет.

Подпись нужна там, где ориентиров одного вида несколько. Два nav без подписей программа чтения объявит одинаково, и человеку придётся заходить в оба, чтобы понять, какой где.

Имя ориентиру даёт и другой атрибут, aria-labelledby. Заголовку дают атрибут id с уникальным на странице именем, aria-labelledby ссылается на это имя, и ориентир называется текстом заголовка. Удобно, когда заголовок в разделе уже есть. Сам по себе заголовок внутри <section> раздел не называет. Браузер понимает оба атрибута, а прибор этой статьи — только aria-label.

Раз смысл дают теги, рано или поздно хочется придумать свои: <card> для карточки, <price> для цены. К этому подталкивает сама вёрстка — класс card встречается в проекте сорок раз, и кажется естественным сделать из него тег. Браузер не возражает: ни ошибки, ни предупреждения. Сравним выдуманный <card> с двумя настоящими тегами. Прогноз: какой display окажется у каждого из трёх?

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
разбор вывода
<section> и <search> — блоки. Так их рисует лист стилей браузера — правила, которые действуют на любой странице ещё до ваших: для обоих тегов в нём записано display: block. Про <card> там ничего нет, и он получил значение по умолчанию — inline, как слово в тексте. Смысла у него тоже нет: ориентиром он не станет, и программа чтения прочтёт только текст внутри. Опечатка <haeder> ведёт себя точно так же: страница не сломается, просто шапка молча перестанет быть шапкой.

Ровно то же случается с настоящим тегом в старом браузере. <search> — самый молодой из восьми: в основных браузерах он появился осенью 2023 года, тогда как остальные семь работают везде давно. Браузер, выпущенный раньше, не знает <search> точно так же, как <card>: рисует строчным и ориентира не даёт. С 13 апреля 2026 года у <search> статус Baseline Widely available — пишите спокойно.

Свои теги в HTML бывают, но только с дефисом в имени, например coffee-card, и поведение им даёт скрипт. Слово без дефиса — просто незнакомый тег.

Второй список, который просит программа чтения, — заголовки. Шесть тегов от <h1> до <h6> — шесть уровней оглавления: <h1> — тема страницы, <h2> — её разделы, <h3> — подразделы внутри раздела. Человек слушает заголовки подряд, как оглавление книги, или прыгает по уровню: в NVDA клавиша 2 ведёт к следующему <h2>.

Оглавление ломается, когда уровень перепрыгивает ступеньку: после <h1> сразу <h3>. Слушатель решит, что пропустил раздел, и пойдёт его искать — или собьётся и перестанет понимать, где находится. Список заголовков идёт по порядку, а такой прыжок отмечает стрелкой. Прогноз: у каких строк стрелка будет?

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
разбор вывода
Стрелки у двух строк: <h3> после <h1> и <h4> после <h2>. Переход от «Новинок» к «Меню», с третьего уровня на второй, — не прыжок: подниматься можно на сколько угодно ступенек, это просто конец подраздела. <section> вокруг «Новинок» ничего не изменил: headings смотрит только на цифру в теге, и программа чтения тоже.

Прыжок почти всегда появляется ради размера: <h4> мельче, и его берут для скромного подзаголовка. Но размер — дело стилей, а уровень — место в оглавлении. Нужен мелкий <h2> — ему уменьшают шрифт в CSS; языку стилей посвящена вторая глава пролога.

Откуда вообще взялась мысль, что <section> меняет уровень? Когда-то стандарт обещал: пишите в каждом <section> свой <h1>, а браузер сам пересчитает уровень по вложенности и превратит его в <h2>. Обещание не выполнил ни один браузер, но проверить его след стоит: замер сравнит размер шрифта у заголовка снаружи раздела и внутри него. Одинаков или нет?

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
разбор вывода
Одинаковый: 28px, вдвое крупнее обычного текста рамки. Пересчёта нет, и не только в размере. Алгоритм, который должен был его делать, много лет числился в стандарте, но ни браузеры, ни программы чтения его так и не реализовали, и из стандарта его убрали. <h1> внутри раздела — по-прежнему первый уровень, и в оглавлении страницы просто появляется второй заголовок первого уровня.

От обещания остался след в стилях браузеров. Они рисовали <h1> внутри <section>, <article>, <aside> и <nav> мельче, размером <h2>, и глаз видел пересчёт, которого для программы чтения не было. Сейчас след стирают: Chrome и Firefox рисуют такие заголовки одним размером, как в примере, а в браузере постарше второй <h1> может выйти мельче. Уровень от размера не зависит ни там, ни там.

Уровень заголовка выбирают по месту в оглавлении, а не по размеру: одна тема страницы — один h1, разделы — h2, и вниз без прыжков.

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

Возьмите index.html из первой задачи статьи про скелет страницы и допишите в <body> шапку с меню, <main> и подвал. Откройте файл в Chrome и нажмите F12 (на Mac — Cmd+Option+I). Во вкладке Elements выделите <header>, а в панели справа, там же, где Styles, откройте вкладку Accessibility — в узкой панели она прячется за кнопкой ». Среди вычисленных свойств, Computed properties, окажется роль banner: вот так браузер пересказал ваш тег программе чтения. Переключатель Show accessibility tree в той же вкладке показывает в Elements дерево доступности вместо дерева элементов, и все ориентиры страницы видны разом. В Firefox дерево доступности — отдельная вкладка Accessibility.

Настоящая карта подробнее прибора. Для страницы из большого примера Chrome покажет у разделов без подписи и у врезки внутри раздела роль generic — «ничего особенного». Шапка и подвал статьи получат роли sectionheader и sectionfooter, шапка и подвал раздела. Роль form Chrome пишет у любой формы, подписанной или нет. Остальные ориентиры и их подписи — те же, что напечатал прибор.

Роль тега можно переписать атрибутом role, и в дереве это тоже видно. На ролях держится и старый запрет вёрстки таблицами: колонки страницы из <table> плохи не на вкус, а на слух — без role="presentation", который снимает с таблицы её роль, программа чтения может объявить раскладку таблицей данных и начать считать строки и столбцы.

Лучшая проверка — послушать собственную страницу. NVDA бесплатна, а на Mac уже встроен VoiceOver: Cmd+F5 включает его, а Control+Option+U открывает ротор — списки заголовков, ориентиров и ссылок. Пять минут со своей страницей и закрытыми глазами объясняют смысл тегов лучше любой таблицы: вы физически почувствуете, каково это — искать меню наугад.

Ориентиры задают смысл и порядок страницы, а расставить их на экране — шапку сверху, меню сбоку — дело стилей. Во второй главе пролога из этих же тегов соберётся раскладка страницы на сетке, и там же видно, что сетка переставляет области только на экране: программа чтения по-прежнему идёт по разметке. А сами инструменты разработчика, вкладка Elements и блок Styles разобраны в части II — в статье про инспектор элементов.

  • Вид задают стили, смысл — теги. <div> и <header> на экране не отличить, но ориентир banner даёт только <header>: программа чтения собирает карту страницы из тегов.
  • <header> — banner и <footer> — contentinfo, если не лежат внутри article, aside, main, nav или section. <nav>, <main> и <search> — ориентиры всегда, <aside> — на уровне страницы и в <main>. <section> и <form> становятся ориентирами с подписью, <article> и <div> ориентиров не дают.
  • Подпись aria-label нужна, когда ориентиров одного вида несколько: два меню без неё звучат одинаково.
  • Незнакомый тег браузер не ругает: <card> и опечатка <haeder> рисуются строчными элементами без смысла. <search> — Baseline Widely available с апреля 2026 года.
  • Заголовки <h1>–<h6> — оглавление: уровень выбирают по месту, а не по размеру, и вниз не прыгают. <section> уровень не пересчитывает, <h1> внутри него остаётся первым уровнем.
  • Прибор — модель из таблицы. Настоящую карту показывает дерево доступности во вкладке Accessibility инструментов разработчика, а лучшая проверка — послушать страницу в NVDA или VoiceOver.

1/4<header> стоит внутри <article>, а статья — прямо в <body>. Что напечатает для этой шапки landmarks?

2/4Какой из элементов прибор напечатает как region?

3/4Внутри <section> стоит <h1>Меню</h1>. Какой это уровень для программы чтения?

4/4В разметке опечатка: <haeder>Кофейня</haeder>. Что будет?

Кофейня из div

Страницу кофейни сверстали одними <div>: наверху название и меню, потом основное содержимое с акцией недели, внизу подвал. Предскажите, что напечатают landmarks и headings, и запустите пример. Потом замените блоки <div> тегами из таблицы. Акция — не главное содержимое, её место сбоку.

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
разбор вывода
Вывод должен стать таким: banner, · navigation, main, · complementary, contentinfo, h1 Кофе с собой, h2 Акция недели.

Внешний блок с названием — шапка, <header>, а ссылки в нём — <nav>. Блок с <h1> — <main>. Акция внутри него — <aside>: внутри <main> врезка остаётся ориентиром. Последний блок — <footer>.

jsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

До замены landmarks не напечатал ничего: у <div> ориентиров не бывает, а заголовки на месте. После замены точки стоят у navigation и complementary: меню лежит в шапке, акция — в основном содержимом. Абзацы остались абзацами: ориентиры — крупные области страницы, а не каждый кусок текста.

Два меню и раздел скидок

На странице два меню — разделы сайта наверху и документы в подвале — и раздел скидок, к которому хочется прыгать сразу. Предскажите вывод: различит ли прибор два меню и найдётся ли среди ориентиров раздел скидок? Потом подпишите оба меню и раздел, не меняя тегов.

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Вывод должен стать таким: navigation «Разделы», main, · region «Скидки», contentinfo, · navigation «Документы».

Всем троим нужен aria-label. Меню и так были ориентирами, но оба печатались одинаково — navigation, и так же одинаково их объявила бы программа чтения. Подпись их различает. Раздел без подписи ориентиром не был, с подписью он стал region.

jsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Подвал печатается до своего меню: строки идут в порядке разметки, а <footer> открывается раньше вложенного в него <nav>. В настоящем браузере разделу можно было дать и aria-labelledby со ссылкой на id его заголовка <h2>: имя возьмётся из текста заголовка. Прибор этот атрибут не читает.

Оглавление без прыжков

Автор меню кофейни рассчитывал, что <section> сам опустит свой <h1> на ступеньку, а <h4> взял потому, что он мельче. Предскажите вывод, запустите пример и расставьте уровни так, чтобы в оглавлении не осталось ни прыжков, ни второго заголовка первого уровня.

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
разбор вывода
Вывод должен стать таким: h1 Кофейня «Зерно», h2 Меню, h3 Кофе, h3 Десерты, h2 Контакты.

Стрелка в исходном выводе одна — у первого <h4>, который шёл сразу после <h1>. Второй <h4> стоял после такого же, а <h3> после <h4> — подъём, не прыжок. Но оглавление всё равно врало: «Меню» было вторым первым уровнем.

Меню — раздел страницы, значит <h2>, хоть он и стоит в <section>: раздел уровней не пересчитывает. Кофе и десерты — подразделы меню, <h3>. Контакты — такой же раздел, как меню, <h2>.

jsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Если подзаголовки нужны мельче, их размер меняют в стилях, а уровень оставляют по месту в оглавлении.