ARIA атрибуты

ARIA-атрибуты в компонентах выбора даты определяют доступность календаря для экранных дикторов и клавиатурной навигации, формируя корректную семантическую модель интерфейса поверх визуального представления. В контексте Pikaday ключевая задача ARIA — обеспечить интерпретацию всплывающего календаря как управляемого виджета с состояниями, фокусом и выбором элементов, а не как набора произвольных DOM-узлов.

Pikaday реализует календарь как всплывающий слой, связанный с текстовым полем ввода. С точки зрения доступности этот слой должен восприниматься как диалоговое окно или как всплывающий интерактивный компонент, управляемый через триггер.

Основные роли ARIA:

  • role="dialog" — обозначает контейнер календаря как модальное или немодальное диалоговое окно
  • role="application" (используется реже) — указывает на сложный интерактивный компонент с собственной логикой клавиатуры
  • role="grid" — календарная сетка дней месяца
  • role="gridcell" — отдельная ячейка дня
  • role="button" — элементы управления переключением месяцев

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

Связь поля ввода и календаря

Связывание текстового поля и всплывающего календаря реализуется через набор атрибутов, обеспечивающих контекст и управление:

  • aria-haspopup="dialog" — указывает, что поле открывает всплывающий диалог
  • aria-expanded="true|false" — отражает текущее состояние открытия календаря
  • aria-controls="pikaday-container" — связывает input с контейнером календаря
  • aria-label — задаёт доступное имя поля или календаря

Типичная структура связи:

<input
  id="date-input"
  aria-haspopup="dialog"
  aria-expanded="false"
  aria-controls="pikaday-calendar"
  aria-label="Дата"
/>

<div id="pikaday-calendar" role="dialog" aria-modal="false">
  ...
</div>

Обновление aria-expanded синхронизируется с состоянием открытия календаря. Это позволяет вспомогательным технологиям отслеживать жизненный цикл компонента.

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

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

Для этого применяется модель aria-activedescendant:

  • фокус остаётся на контейнере grid
  • активная ячейка определяется через id
  • экранный диктор получает информацию о текущем выбранном элементе

Пример:

<div
  class="pika-single"
  role="grid"
  aria-activedescendant="pika-day-2026-06-15"
  tabindex="0"
>
  <div id="pika-day-2026-06-15" role="gridcell" aria-selected="true">
    15
  </div>
</div>

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

Состояния элементов календаря

Каждая ячейка дня должна отражать своё состояние через ARIA:

  • aria-selected="true" — выбранная дата
  • aria-disabled="true" — недоступная дата (например, вне диапазона min/max)
  • aria-current="date" — текущая дата (сегодня)
  • aria-label — полное текстовое описание даты

Особое значение имеет aria-label, поскольку визуальное представление даты (например, «15») недостаточно информативно.

Пример:

<div
  id="pika-day-2026-06-15"
  role="gridcell"
  aria-selected="false"
  aria-disabled="false"
  aria-current="date"
  aria-label="15 июня 2026"
>
  15
</div>

Навигация по месяцам и aria-label на управляющих элементах

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

Используемые атрибуты:

  • aria-label="Следующий месяц"
  • aria-label="Предыдущий месяц"
  • aria-controls="pikaday-calendar"

Пример:

<button
  class="pika-next"
  aria-label="Следующий месяц"
  aria-controls="pikaday-calendar"
>
  ›
</button>

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

aria-live и динамические обновления

Календарь Pikaday динамически обновляет содержимое при переключении месяцев. Для корректного уведомления вспомогательных технологий применяется aria-live:

  • aria-live="polite" — мягкое уведомление о смене месяца
  • aria-atomic="true" — озвучивание полного блока при обновлении

Пример контейнера заголовка месяца:

<div class="pika-title" aria-live="polite" aria-atomic="true">
  Июнь 2026
</div>

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

tabindex и управление фокусируемостью

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

  • tabindex="0" — включение контейнера в порядок фокуса
  • tabindex="-1" — исключение вспомогательных элементов из табуляции

Основной фокус обычно удерживается на grid-контейнере:

<div class="pika-single" role="grid" tabindex="0">
  ...
</div>

Отдельные дни не получают tabindex, поскольку навигация реализуется стрелками клавиатуры внутри grid-модели.

aria-hidden и управление видимостью

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

  • aria-hidden="true" — скрытие закрытого календаря
  • удаление из DOM или переключение класса visibility также допустимо, но ARIA-метка критична
<div id="pikaday-calendar" role="dialog" aria-hidden="true">

При открытии значение переключается:

aria-hidden="false"

Это предотвращает ситуацию, когда скрытый календарь остаётся доступным для экранного диктора.

Модель выбора даты и aria-pressed

В некоторых реализациях Pikaday используется модель кнопок вместо gridcell. В этом случае применим aria-pressed:

  • aria-pressed="true" — активная кнопка даты
  • aria-pressed="false" — неактивная

Однако grid-модель предпочтительнее, так как она семантически ближе к таблице календаря.

Контекстные группы и aria-labelledby

Для объединения частей календаря используются связи:

  • aria-labelledby — связывает заголовок месяца с контейнером
  • aria-describedby — добавляет дополнительное описание

Пример:

<div id="pika-header" class="pika-title">
  Июнь 2026
</div>

<div
  class="pika-single"
  role="grid"
  aria-labelledby="pika-header"
>

Это создаёт логическую связь между визуальным заголовком и структурой календаря.

Управление состояниями диапазонов дат

При использовании ограничений minDate и maxDate каждая неактивная дата должна отражать недоступность:

  • aria-disabled="true"
  • отсутствие возможности получения aria-selected="true"

Дополнительно может использоваться aria-label, уточняющий причину:

aria-label="10 июня 2026, недоступно"

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

Клавиатурная модель и ARIA-согласованность

ARIA-структура должна соответствовать логике клавиатурного управления:

  • стрелки перемещают aria-activedescendant
  • Enter выбирает aria-selected
  • Escape закрывает dialog и возвращает aria-expanded="false"

Несоответствие между состоянием ARIA и фактической логикой приводит к нарушению доступности: экранный диктор начинает озвучивать устаревшие состояния интерфейса.

Итоговая структура ARIA для календаря Pikaday

Комплексная модель включает:

  • role="dialog" для контейнера
  • role="grid" для календарной сетки
  • role="gridcell" для дней
  • aria-selected, aria-disabled, aria-current для состояний
  • aria-activedescendant для управления фокусом
  • aria-expanded, aria-controls для связи с input
  • aria-live для динамических обновлений
  • aria-label для семантического описания элементов

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