Перейти к содержимому
Pandus

Доступное меню навигации

Обновлено 14 октября 2026 г. · 6 мин чтения

Главная навигация — первое, чем пользуется каждый посетитель, а в магазине она часто разрастается в мегаменю с категориями, брендами и акциями. Самая частая ошибка доступности — не отсутствие ARIA, а неправильный ARIA: role="menubar", скопированный из библиотеки компонентов. На этой странице объясняется, что APG рекомендует вместо этого. Она входит в серию доступных UI-паттернов.

В APG есть шаблон Menu и Menubar для меню приложений — «списка вариантов, таких как действия или функции», который ведёт себя как меню в настольных программах. В его примере навигационного menubar есть явное предупреждение:

«Шаблон menubar требует сложной функциональности, ненужной для обычной навигации по сайту» … «Шаблон, лучше подходящий для обычной навигации по сайту с раскрывающимися группами ссылок, — это шаблон раскрытия (Disclosure)».

APG добавляет, что шаблон раскрытия «лучше подходит для большинства сайтов, потому что немногим сайтам нужна дополнительная функциональность клавиатуры». Его пример навигации с раскрытием не использует роль menu, потому что он «не обеспечивает сложной функциональности, которую ожидают ассистивные технологии». role="menu" — это обещание поведения меню в стиле настольных программ; если сайт его не выполняет, такое меню пользоваться сложнее, чем обычными ссылками.

Рекомендация: ориентир nav, вложенные списки ссылок и кнопки раскрытия для групп.

Применимые критерии WCAG

Пример кода

<nav aria-label="Основная">
  <ul>
    <li>
      <button type="button" aria-expanded="false" aria-controls="nav-women">Женщинам</button>
      <ul id="nav-women" hidden>
        <li><a href="/ru/women/dresses">Платья</a></li>
        <li><a href="/ru/women/shoes" aria-current="page">Обувь</a></li>
      </ul>
    </li>
    <li><a href="/ru/sale">Распродажа</a></li>
  </ul>
</nav>

<script>
  document.querySelectorAll('nav button[aria-expanded]').forEach((button) => {
    const list = document.getElementById(button.getAttribute('aria-controls'));
    button.addEventListener('click', () => {
      const open = button.getAttribute('aria-expanded') === 'true';
      button.setAttribute('aria-expanded', String(!open));
      list.hidden = open;
    });
  });
</script>

Замечания:

  • Если пункт верхнего уровня должен быть ещё и ссылкой на страницу категории, у APG есть вариант «Disclosure Navigation Menu with Top-Level Links» («Меню навигации с раскрытием со ссылками верхнего уровня»): ссылка и отдельная кнопка-переключатель стоят рядом. Не делайте один элемент одновременно ссылкой и переключателем.
  • Добавьте Escape: закрывать открытую группу и возвращать фокус на её кнопку, как в примере APG.
  • На мобильном кнопка-«гамбургер» — это то же раскрытие: aria-expanded, имя вроде «Меню», а панель следует за ней в DOM.

Работа с клавиатуры

Из примера навигации с раскрытием из APG:

Клавиша Действие
Tab / Shift + Tab Перемещает по кнопкам и ссылкам верхнего уровня и, когда группа открыта, по её ссылкам
Enter или пробел на кнопке Открывает или закрывает группу
Enter на ссылке Переходит по ссылке
Escape Закрывает открытую группу и возвращает фокус на её кнопку
Клавиши со стрелками, Home, End Необязательно: перемещают между кнопками и ссылками; дополняют Tab, а не заменяют его

Настоящему menubar, напротив, понадобились бы стрелки для перемещения между пунктами, Tab для выхода из всего menubar и быстрый поиск по первой букве — та самая «сложная функциональность», о которой говорит APG.

Частые ошибки в интернет-магазинах

  • Мегаменю, которые открываются только при наведении мыши; пользователи клавиатуры проходят Tab по ссылкам верхнего уровня и никогда не добираются до подкатегорий.
  • role="menubar" и role="menuitem" на обычных ссылках, часто из темы или плагина, без работы со стрелками.
  • Скрытые подменю, которые всё ещё получают фокус: Tab пропадает в невидимых ссылках (фокус теряется за opacity: 0).
  • Название категории — одновременно ссылка и переключатель, так что пользователи касания и клавиатуры не могут открыть подменю.
  • Нет aria-expanded или он никогда не меняется с false.
  • Несколько ориентиров nav без подписей (основной, подвал, личный кабинет), и пользователи экранных дикторов не могут их различить.
  • Кнопки-«гамбургеры» без имени и мобильные меню, которые при реализации в виде полноэкранного наложения не удерживают и не возвращают фокус.

Что находит автоматика и что остаётся для ручной проверки

Автоматическая (правило axe-core) Что находит
link-name, button-name Ссылки меню и кнопки-переключатели или «гамбургеры» без имени
aria-required-children, aria-required-parent menu/menubar без дочерних menuitem, menuitem вне меню
aria-allowed-role Роли, не подходящие элементу (рекомендация)
aria-hidden-focus Ссылки внутри подменю с aria-hidden, которые всё ещё получают фокус
list, listitem Нарушенная структура списка в навигации
landmark-unique Несколько ориентиров навигации с одинаковым именем или без него (рекомендация)
bypass, skip-link Нет способа пропустить меню; ссылка «перейти к содержимому» с неверной целью
target-size Слишком мелкие цели меню

Все они — карточки в нашей библиотеке правил. Сканирование видит страницу такой, какой она загружается, с закрытыми подменю. Ручная проверка: откройте каждую группу с Enter или пробелом, убедитесь, что aria-expanded меняется, нажмите Escape, пройдите Tab по открытой группе, проверьте, что закрытые группы не получают фокус, и повторите с мобильным меню при ширине 320 CSS-пикселей. Расширение для браузера может проверить страницу с открытой группой.

Запустите бесплатную проверку одной страницы на вашей главной. Это автоматическая проверка, а не полный аудит.

Частые вопросы

Стоит ли использовать для навигации сайта role="menu" или role="menubar"?

Обычно нет. В APG предупреждают, что шаблон menubar требует сложной функциональности, ненужной для обычной навигации по сайту, и рекомендуют шаблон раскрытия (disclosure) для навигации с раскрывающимися группами ссылок.

Что такое навигация с раскрытием (disclosure)?

Элемент nav со списком ссылок, где у каждой группы с вложенными ссылками есть кнопка с aria-expanded, которая показывает или скрывает вложенный список. Пользователи перемещаются по нему с помощью Tab, Enter и пробела, как и по остальной странице.

Как отметить в меню текущую страницу?

Поставьте aria-current="page" на ссылку на страницу, на которой находится пользователь, и покажите её ещё и визуально, не только цветом.

Нужна ли в меню поддержка клавиш со стрелками?

Не в шаблоне disclosure. Пример APG поддерживает стрелки, Home и End как необязательное дополнение к Tab. Настоящий menubar, напротив, должен реализовывать полную модель работы со стрелками.

Может ли автоматическая проверка проверить меню навигации?

Она находит ссылки и кнопки без имён, роли меню без пунктов меню, повторяющиеся неподписанные ориентиры навигации и скрытые, но получающие фокус ссылки. Открываются ли подменю с клавиатуры и обновляется ли aria-expanded, она проверить не может.

Источники

  • W3C WAI, APG Navigation Menubar example (caution about site navigation): https://www.w3.org/WAI/ARIA/apg/patterns/menubar/examples/menubar-navigation/
  • W3C WAI, APG Menu and Menubar pattern: https://www.w3.org/WAI/ARIA/apg/patterns/menubar/
  • W3C WAI, APG Disclosure pattern: https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/
  • W3C WAI, APG Disclosure Navigation Menu example: https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-navigation/
  • W3C WAI, APG Menu Button pattern: https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/