Аккордеоны с множественным раскрытием

Интерфейсный компонент «аккордеон» представляет собой набор связанных секций, каждая из которых содержит заголовок и скрываемую область содержимого. При взаимодействии с заголовком соответствующий блок раскрывается или сворачивается. В отличие от классического аккордеона, где допускается открытие только одной секции одновременно, аккордеон с множественным раскрытием допускает одновременное отображение нескольких панелей.

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

  • документация с несколькими независимыми разделами;
  • списки параметров;
  • интерфейсы настроек;
  • страницы FAQ.

С точки зрения доступности подобные интерфейсы требуют корректной реализации клавиатурной навигации, управления фокусом и правильной семантики. Библиотека ally.js предоставляет набор инструментов для решения этих задач.


Особенности доступности аккордеонов

При реализации доступного аккордеона необходимо учитывать несколько важных аспектов.

Семантическая структура

Каждая секция состоит из двух элементов:

  1. Кнопка управления — элемент, активирующий раскрытие.
  2. Панель содержимого — область, отображаемая или скрываемая.

Обычно используется следующая схема:

button → управляет panel

Кнопка должна иметь связь с панелью через атрибуты:

  • aria-controls
  • aria-expanded

Панель содержимого содержит:

  • id, на который ссылается кнопка
  • role="region" или аналогичную семантику

Поведение клавиатуры

Стандартная доступная модель предполагает поддержку:

  • Tab — переход между кнопками
  • Enter / Space — раскрытие или сворачивание панели
  • Arrow Up / Arrow Down — навигация между заголовками

Библиотека ally.js упрощает реализацию корректной обработки фокуса и управления интерактивностью.


Назначение ally.js в контексте аккордеонов

Библиотека ally.js предназначена для управления доступностью интерфейсов. В аккордеонах она применяется для решения следующих задач:

  • контроль фокусируемости элементов;
  • управление интерактивными состояниями;
  • корректная обработка скрытых элементов;
  • предотвращение попадания фокуса в недоступные области.

Особенно важно корректно управлять фокусом, когда панели скрываются или отображаются.

Если панель скрыта через CSS (display: none или hidden), вложенные элементы не должны оставаться доступными для клавиатурной навигации. ally.js позволяет динамически изменять их состояние.


Базовая структура HTML

Аккордеон с множественным раскрытием может иметь следующую разметку:

<div class="accordion">

  <h3>
    <button
      class="accordion-toggle"
      aria-expanded="false"
      aria-controls="panel-1"
      id="accordion-1">
      Раздел 1
    </button>
  </h3>

  <div
    id="panel-1"
    role="region"
    aria-labelledby="accordion-1"
    hidden>

    <p>Содержимое первой панели.</p>

  </div>

  <h3>
    <button
      class="accordion-toggle"
      aria-expanded="false"
      aria-controls="panel-2"
      id="accordion-2">
      Раздел 2
    </button>
  </h3>

  <div
    id="panel-2"
    role="region"
    aria-labelledby="accordion-2"
    hidden>

    <p>Содержимое второй панели.</p>

  </div>

</div>

Ключевые элементы:

  • кнопка управляет состоянием панели;
  • панель скрывается атрибутом hidden;
  • связь осуществляется через aria-controls и aria-labelledby.

Подключение ally.js

Библиотека может подключаться через пакетный менеджер или CDN.

Пример подключения:

<script src="https://unpkg.com/ally.js/ally.min.js"></script>

После загрузки становятся доступны инструменты управления доступностью.


Управление раскрытием панелей

Аккордеон с множественным раскрытием не требует закрытия других секций. Каждая кнопка управляет только своей панелью.

Базовая логика переключения:

const toggles = document.querySelectorAll('.accordion-toggle');

toggles.forEach(button => {
  button.addEventListener('click', () => {

    const expanded = button.getAttribute('aria-expanded') === 'true';
    const panel = document.getElementById(button.getAttribute('aria-controls'));

    button.setAttribute('aria-expanded', !expanded);

    if (expanded) {
      panel.hidden = true;
    } else {
      panel.hidden = false;
    }

  });
});

Каждая секция функционирует независимо.


Контроль фокусируемости скрытых элементов

При скрытии панели важно исключить вложенные элементы из навигации. ally.js предоставляет модуль maintain.disabled, который позволяет временно отключать интерактивность элементов.

Пример:

import maintainDisabled from 'ally.js/maintain/disabled';

После закрытия панели можно отключить доступ к вложенным элементам.

maintainDisabled({
  context: panel
});

При повторном открытии панели ограничения снимаются.

Это предотвращает попадание фокуса на элементы внутри скрытого содержимого.


Использование ally.js для проверки фокусируемых элементов

Библиотека содержит утилиту:

ally.query.focusable

Она позволяет получить список элементов, доступных для фокуса внутри панели.

Пример:

import queryFocusable from 'ally.js/query/focusable';

const elements = queryFocusable({
  context: panel
});

Полученный список можно использовать для:

  • отключения интерактивности;
  • настройки клавиатурной навигации;
  • проверки корректности интерфейса.

Улучшение клавиатурной навигации

Для аккордеонов часто реализуется перемещение между заголовками при помощи стрелок.

Пример обработки:

const buttons = Array.from(document.querySelectorAll('.accordion-toggle'));

buttons.forEach((button, index) => {

  button.addEventListener('keydown', event => {

    if (event.key === 'ArrowDown') {
      event.preventDefault();
      const next = buttons[index + 1] || buttons[0];
      next.focus();
    }

    if (event.key === 'ArrowUp') {
      event.preventDefault();
      const prev = buttons[index - 1] || buttons[buttons.length - 1];
      prev.focus();
    }

  });

});

Фокус циклически перемещается между кнопками.


Управление видимостью через ally.js

В ally.js присутствует инструмент:

ally.element.disabled

Он позволяет программно включать или отключать интерактивность элемента.

Пример:

import elementDisabled from 'ally.js/element/disabled';

elementDisabled(panel, true);

Это делает панель недоступной для взаимодействия.

При открытии:

elementDisabled(panel, false);

Таким образом обеспечивается корректная работа интерфейса для клавиатурной навигации и вспомогательных технологий.


Управление фокусом при раскрытии панели

При открытии панели часто возникает необходимость перенести фокус к первому интерактивному элементу внутри.

В ally.js используется утилита:

ally.query.firstTabbable

Пример:

import firstTabbable from 'ally.js/query/first-tabbable';

const element = firstTabbable({
  context: panel
});

if (element) {
  element.focus();
}

Это особенно полезно в длинных панелях с формами или интерактивными компонентами.


Анимации раскрытия и влияние на доступность

При использовании CSS-анимаций необходимо учитывать, что:

  • элементы не должны оставаться фокусируемыми до завершения анимации;
  • скрытые элементы должны быть исключены из дерева доступности.

Распространённый подход:

  1. отключить интерактивность панели;
  2. запустить анимацию раскрытия;
  3. после завершения вернуть интерактивность.

ally.js облегчает управление этими состояниями.


Работа с большим количеством секций

При наличии десятков секций важно учитывать производительность и управление состоянием.

Оптимизации:

  • делегирование событий;
  • хранение состояния панелей;
  • ленивое подключение интерактивных элементов.

Пример делегирования:

document.querySelector('.accordion')
  .addEventListener('click', event => {

    const button = event.target.closest('.accordion-toggle');

    if (!button) return;

    const panel = document.getElementById(button.getAttribute('aria-controls'));
    const expanded = button.getAttribute('aria-expanded') === 'true';

    button.setAttribute('aria-expanded', !expanded);
    panel.hidden = expanded;

});

Это уменьшает количество обработчиков.


Проверка доступности интерфейса

После реализации аккордеона рекомендуется проверить:

  • корректность aria-expanded;
  • наличие связи aria-controls;
  • исключение скрытых элементов из фокуса;
  • корректную навигацию стрелками;
  • работу Tab-переходов.

ally.js можно использовать для диагностики:

  • поиска фокусируемых элементов;
  • определения интерактивности;
  • проверки доступности DOM-структуры.

Поведение вспомогательных технологий

Экранные дикторы воспринимают кнопку аккордеона следующим образом:

  • объявляется текст кнопки;
  • озвучивается состояние: «развернуто» или «свернуто».

Атрибут aria-expanded обеспечивает передачу этой информации.

Панель с role="region" и aria-labelledby объявляется как область, связанная с заголовком.

Такая структура позволяет пользователю вспомогательных технологий понимать структуру аккордеона и управлять его секциями независимо.


Типичные ошибки реализации

Распространённые проблемы:

Использование ссылок вместо кнопок

<a href="#">Раздел</a>

Это нарушает семантику. Управляющий элемент должен быть button.

Отсутствие aria-expanded

Без этого атрибута экранный диктор не сообщает состояние панели.

Фокус внутри скрытых панелей

Если элементы остаются доступными для Tab-навигации, пользователь может попасть в невидимую область.

Неправильная обработка клавиатуры

Некоторые реализации поддерживают только мышь, игнорируя клавиатуру.


Архитектурный подход

В крупных интерфейсах аккордеон реализуется как модуль:

  • управление состоянием;
  • контроль доступности;
  • интеграция ally.js;
  • обработка событий.

Структура может включать:

Accordion
 ├─ AccordionItem
 │   ├─ ToggleButton
 │   └─ Panel
 └─ AccessibilityController

Контроллер доступности взаимодействует с ally.js и управляет:

  • фокусом;
  • интерактивностью;
  • состояниями элементов.

Масштабирование интерфейса

Аккордеоны с множественным раскрытием часто используются в:

  • системах документации;
  • административных панелях;
  • интерфейсах конфигурации;
  • мобильных интерфейсах.

При масштабировании важно учитывать:

  • оптимизацию DOM;
  • управление состоянием большого количества панелей;
  • корректную работу с ассистивными технологиями.

Библиотека ally.js обеспечивает низкоуровневые инструменты для реализации этих требований, позволяя создавать гибкие и доступные интерфейсы с большим количеством независимых секций.