aria-expanded

aria-expanded – атрибут WAI-ARIA со значениями true/false: сообщает скринридеру, раскрыт ли блок, которым управляет элемент. Ставится на кнопку-триггер аккордеона или меню, а не на скрываемую панель.

4 минуты чтения

Атрибут aria-expanded – состояние (state) из спецификации WAI-ARIA (см. ARIA), которое сообщает вспомогательным технологиям, раскрыт или свёрнут блок, управляемый элементом. Зрячий пользователь видит, что панель аккордеона закрыта, а мобильное меню спрятано; скринридер без атрибута объявит просто «кнопка» – и пользователь не узнает ни того, что кнопка что-то разворачивает, ни текущего состояния. С aria-expanded та же кнопка озвучивается как «кнопка, свёрнуто» или «кнопка, развёрнуто» – состояние понятно до клика. Это обязательный атрибут в интерактивных паттернах доступности (a11y): аккордеон, раскрывашка (disclosure), выпадающее и бургер-меню, combobox, разворачиваемые строки таблиц.

Как это работает

Ключевое правило, в котором ошибаются чаще всего: aria-expanded ставится на управляющий элемент – фокусируемую кнопку-триггер, а не на раскрываемую панель. Атрибут описывает контрол («эта кнопка разворачивает контент, сейчас он скрыт»), а сам блок прячут обычными средствами – атрибутом hidden или display:none.

<!-- Ошибка: атрибут на панели -->
<button>Подробнее</button>
<div class="panel" aria-expanded="true">…</div>

<!-- Правильно: атрибут на кнопке -->
<button aria-expanded="false" aria-controls="panel">Подробнее</button>
<div id="panel" hidden>…</div>
Значения aria-expanded
ЗначениеЧто означает
trueУправляемый блок раскрыт и отображается
falseУправляемый блок свёрнут; в закрытом состоянии значение проставляют явно, а не удаляют атрибут
Атрибута нетСостояние undefined: элемент вообще не управляет раскрываемым блоком

Семантика undefined важна на практике. Атрибут нельзя вешать «на всякий случай» на элементы, которые ничем не управляют: само его наличие заявляет скринридеру, что перед пользователем раскрывающий контрол. По той же причине в дереве (tree) true или false несут только родительские узлы, а листья остаются без атрибута – иначе они будут объявлены «родителями» без потомков. Исключение из правила «нет блока – нет атрибута» одно: у combobox значение по умолчанию – aria-expanded="false", свёрнутый список считается нормальным стартовым состоянием.

aria-expanded и aria-controls – как связаны

aria-expanded отвечает на вопрос «раскрыто ли», aria-controls – «что именно»: в него записывают id управляемой панели. Требования W3C ARIA Authoring Practices Guide (APG) различаются по паттернам: в аккордеоне aria-controls на кнопке-заголовке обязателен, в простом disclosure («показать ещё») – опционален. Переключение состояния – всегда работа JavaScript: по клику скрипт меняет true на false и обратно и синхронно прячет или показывает панель. Если содержимое раскрывается через полную перезагрузку страницы, динамическое переключение не нужно.

Зачем это нужно

Главная ценность – предсказуемость интерфейса. Пользователь скринридера слышит состояние до взаимодействия, а после клика получает подтверждение, что действие сработало: фокус остаётся на кнопке, и смена озвучки со «свёрнуто» на «развёрнуто» – единственный внятный сигнал, что панель открылась. Типовые сценарии: аккордеон в FAQ-блоке, бургер и выпадающие подменю в шапке, фильтры каталога «показать все», строки таблиц и деревья. Побочные выгоды для разработчика:

  • Стилизация без лишних классов – состояние уже в DOM, поэтому иконку-стрелку поворачивают селектором атрибута: button[aria-expanded="true"] .chevron { transform: rotate(180deg); }. Скрипт меняет один атрибут, а не жонглирует классами вроде .is-open.
  • Нативная альтернатива – элементы details/summary сообщают состояние раскрытия сами, дублировать их aria-expanded не нужно. Самодельный вариант на кнопке выбирают, когда нужен контроль над анимацией, разметкой или логикой «открыта только одна панель».
  • Машиночитаемость – явные состояния и связи по id делают структуру страницы понятнее любым парсерам, а не только скринридерам: та же логика семантики, что работает на GEO.

Пример

Аккордеон для FAQ-блока по паттерну APG – связка, которую я использую на Битрикс-проектах вместе с разметкой FAQPage: видимая часть доступна скринридерам, машиночитаемая – в JSON-LD. Заголовок – элемент h3, внутри него кнопка; панель связана с кнопкой через aria-labelledby:

<h3>
  <button aria-expanded="false" aria-controls="faq1-panel" id="faq1-btn">
    Сколько длится разработка сайта?
  </button>
</h3>
<div id="faq1-panel" role="region" aria-labelledby="faq1-btn" hidden>
  <p>Обычно от четырёх недель…</p>
</div>

Панель получает role="region" и aria-labelledby на кнопку – скринридер связывает содержимое с вопросом. По APG, если раскрытую панель нельзя свернуть (например, последняя открытая), на её кнопку добавляют aria-disabled="true", а не блокируют элемент атрибутом disabled, чтобы не выкидывать его из фокуса. JavaScript-обработчик читает текущее состояние, инвертирует его и синхронно переключает панель:

btn.addEventListener('click', () => {
  const open = btn.getAttribute('aria-expanded') === 'true';
  btn.setAttribute('aria-expanded', String(!open));
  panel.hidden = open;
});

Второй частый случай – бургер мобильного меню. У кнопки нет текста, поэтому доступное имя задаёт aria-label, а состояние – aria-expanded:

<button aria-expanded="false" aria-controls="site-nav" aria-label="Открыть меню">
  <svg aria-hidden="true">…</svg>
</button>
<nav id="site-nav" hidden>…</nav>

В закрытом состоянии панель спрятана атрибутом hidden – меню недоступно и визуально, и для скринридера, пока кнопка не переключит состояние. Клавиатура в обоих паттернах работает нативно: Enter и Space на кнопке переключают панель, Tab идёт по обычному порядку, перехватывать стрелки не нужно. Внутри раскрытого меню активный пункт помечают атрибутом aria-current – три атрибута вместе закрывают базовую доступность навигации сайта.

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

На какой элемент ставить aria-expanded – на кнопку или на раскрываемый блок?

Только на управляющую кнопку-триггер. Атрибут описывает контрол: «эта кнопка разворачивает контент, сейчас он свёрнут». Ставить его на панель – типовая ошибка: скринридер получит ложную семантику, будто сама панель что-то разворачивает. Панель прячут атрибутом hidden или через display:none.

Зачем нужен aria-controls рядом с aria-expanded?

aria-expanded сообщает состояние («раскрыто или нет»), aria-controls – связь («что именно»): в него записывают id управляемой панели. По APG в паттерне аккордеона aria-controls на кнопке-заголовке обязателен, а в простом disclosure – опционален.

Что означает отсутствие aria-expanded?

Состояние undefined: элемент вообще не управляет раскрываемым блоком. Поэтому атрибут не ставят «на всякий случай» – само его наличие заявляет о раскрывающем контроле. В дереве по этой причине листья остаются без атрибута, иначе будут объявлены родительскими узлами без потомков.

Как скринридер озвучивает aria-expanded?

Вместе с ролью и именем: «кнопка, свёрнуто» или «кнопка, развёрнуто» (collapsed/expanded). Пользователь понимает состояние до клика, а после клика смена озвучки подтверждает, что действие сработало – фокус при этом остаётся на кнопке.

Нужен ли aria-expanded, если аккордеон сделан на details/summary?

Нет: нативные details/summary сами сообщают состояние раскрытия вспомогательным технологиям, дублировать его через ARIA не нужно. Самодельный вариант на кнопке с aria-expanded выбирают, когда нужен полный контроль над разметкой, анимацией или логикой «открыта только одна панель».

Как сделать доступное бургер-меню?

Кнопка с aria-expanded="false", aria-controls с id панели меню и aria-label, потому что у бургера нет текста; сама панель скрыта атрибутом hidden. По клику JavaScript переключает aria-expanded на true и убирает hidden. Enter и Space на кнопке работают нативно, дополнительного кода для клавиатуры не нужно.

Можно ли менять иконку-стрелку по aria-expanded без дополнительных классов?

Да, селектором атрибута: button[aria-expanded="true"] .chevron { transform: rotate(180deg); }. Состояние уже есть в DOM, поэтому js-классы вроде .is-open не нужны – скрипт меняет один атрибут, а CSS реагирует на него.

Материалы по теме

Валентина Меланина

Нужна консультация?

Разберу ваш сайт и покажу точки роста

Если хотите понять, как этот термин применить к вашему проекту — начнём с аудита.