Screen readers

Роль экранных читалок в интерактивных картах

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

Картографические библиотеки, основанные на canvas и div-тайлах, не предоставляют семантической информации автоматически. Поэтому доступность формируется через:

  • ARIA-атрибуты
  • текстовые альтернативы
  • структурированные списки объектов
  • управление фокусом
  • скрытые (visually hidden) описания

В Leaflet доступность реализуется частично на уровне ядра и частично на уровне прикладного кода.


Семантическая оболочка карты

Основная проблема карт — отсутствие семантики у визуального слоя. Контейнер карты должен быть интерпретируемым как единый интерактивный объект.

Типичная конфигурация:

  • role="application" или role="region"
  • aria-label с описанием назначения карты
  • tabindex="0" для включения в порядок фокуса
<div id="map"
     role="application"
     aria-label="Интерактивная карта с объектами"
     tabindex="0"></div>

role="application" применяется только при необходимости полного перехвата клавиатуры, поскольку он изменяет поведение экранной читалки, переводя её в режим взаимодействия с приложением.

role="region" предпочтительнее, если карта используется как вспомогательный элемент страницы.


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

Экранные читалки зависят от логического порядка фокуса. Карта как единый элемент не должна «перехватывать» фокус без необходимости, но должна позволять вход и выход из режима взаимодействия.

Основные принципы:

  • вход в карту через клавишу Tab
  • выход через Esc (в кастомной логике)
  • предотвращение «залипания» фокуса внутри слоёв

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

map.getContainer().addEventListener('keydown', (e) => {
  if (e.key === 'Escape') {
    map.getContainer().blur();
  }
});

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


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

Слепые и слабовидящие пользователи не могут интерпретировать визуальные тайлы. Поэтому карта должна дублироваться структурированным списком объектов.

Подходы:

  • список маркеров (ul > li)
  • таблица объектов с координатами
  • описание активного слоя
  • динамическое обновление при перемещении карты

Пример структуры:

<ul aria-label="Список объектов на карте">
  <li>Библиотека — координаты 51.1605, 71.4704</li>
  <li>Музей — координаты 51.1470, 71.4320</li>
</ul>

Такой список может обновляться синхронно с картой.


ARIA-описания маркеров

Каждый маркер должен иметь доступное текстовое представление. В Leaflet маркеры создаются как DOM-элементы, что позволяет назначать атрибуты напрямую.

const marker = L.marker([51.1605, 71.4704])
  .addTo(map)
  .bindTooltip("Библиотека");

marker.getElement().setAttribute("role", "button");
marker.getElement().setAttribute("aria-label", "Библиотека, координаты 51.1605 71.4704");

Важно учитывать:

  • tooltip не заменяет aria-label
  • координаты должны быть представлены текстом
  • роль элемента должна соответствовать интерактивности

Динамические обновления и live regions

Карты постоянно изменяют состояние: перемещение, масштабирование, фильтрация слоёв. Экранные читалки не отслеживают такие изменения автоматически.

Используются aria-live области:

<div aria-live="polite" id="map-status" class="visually-hidden"></div>

Обновления:

map.on('moveend', () => {
  const center = map.getCenter();
  document.getElementById('map-status').textContent =
    `Центр карты изменён: ${center.lat.toFixed(4)}, ${center.lng.toFixed(4)}`;
});

Типы live regions:

  • polite — ненавязчивое обновление
  • assertive — прерывающее (используется редко)

Доступность слоёв и групп объектов

Слои в Leaflet часто содержат множество объектов. Для экранных читалок важна иерархия:

  • слой как логическая группа
  • объекты внутри слоя как элементы списка
  • возможность переключения состояния слоя через клавиатуру

ARIA-структура слоя:

<div role="group" aria-label="Объекты инфраструктуры">
  <button aria-pressed="true">Показать больницы</button>
</div>

Навигация с клавиатуры

Экранные читалки тесно связаны с клавиатурной навигацией. Карта должна поддерживать:

  • перемещение фокуса по интерактивным элементам
  • активацию объектов через Enter и Space
  • логическую последовательность переходов

Рекомендуемая модель:

  • Tab — переход к карте
  • стрелки — перемещение по карте (если реализовано)
  • Enter — активация объекта
  • Esc — выход из режима карты

Обработка масштабирования и изменения вида

Изменение масштаба карты должно иметь текстовое сопровождение. Без этого экранная читалка не фиксирует изменения.

map.on('zoomend', () => {
  const zoom = map.getZoom();
  statusEl.textContent = `Масштаб карты изменён: уровень ${zoom}`;
});

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

  • текущий слой
  • видимая область
  • количество объектов

Альтернативы визуальному взаимодействию

Некоторые функции карты невозможно адекватно интерпретировать через экранную читалку без альтернатив:

  • кластеризация маркеров → список групп
  • тепловые карты → статистическое описание
  • тайловые слои → описание покрытия
  • маршруты → пошаговый список координат

Пример маршрута:

<ol aria-label="Маршрут">
  <li>Старт: 51.1605, 71.4704</li>
  <li>Поворот на север</li>
  <li>Финиш: 51.1470, 71.4320</li>
</ol>

Ограничения стандартного поведения Leaflet

Базовая реализация Leaflet не предоставляет полноценную доступность из коробки:

  • отсутствует встроенная семантика для всех слоёв
  • маркеры не всегда снабжены ARIA-атрибутами
  • нет автоматического списка объектов
  • ограниченная интеграция с экранными читалками

Поэтому доступность формируется поверх библиотеки, а не внутри неё.


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

Технический слой доступности часто использует скрытые элементы:

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}

Используется для:

  • описания состояния карты
  • передачи координат
  • уведомлений об изменениях

Согласованность состояния интерфейса

Экранная читалка воспринимает интерфейс как последовательность состояний. Карта должна сохранять синхронизацию между:

  • визуальным положением
  • DOM-структурой
  • ARIA-сообщениями
  • списком объектов

Несогласованность приводит к потере контекста: пользователь слышит устаревшие данные при актуальном визуальном отображении.


Обработка всплывающих элементов

Popup и tooltip требуют отдельной семантики:

marker.bindPopup("Объект инфраструктуры", {
  ariaLabel: "Информация об объекте инфраструктуры"
});

Рекомендации:

  • popup должен получать фокус при открытии
  • закрытие должно быть доступно с клавиатуры
  • содержимое должно быть текстовым, а не только визуальным

Интеграция с внешними списками и панелями

Наиболее устойчивый подход к доступности карт — разделение:

  • визуальная карта
  • параллельная текстовая панель

Панель синхронизируется с картой:

  • выделение маркера → подсветка элемента списка
  • выбор элемента списка → центрирование карты
  • фильтрация → обновление обоих представлений

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