Календарный компонент в первую очередь взаимодействует не с самим календарём, а с текстовым полем ввода. Именно это поле становится основным источником информации для программ чтения экрана, таких как NVDA, JAWS и VoiceOver.
Корректная доступность начинается с семантики input-элемента:
<label>aria-label или
aria-labelledbyaria-describedbyПример базовой разметки:
<label for="date_input">Дата</label>
<input
id="date_input"
type="text"
aria-describedby="date_help"
>
<div id="date_help">Формат: день.месяц.год</div>
При инициализации Flatpickr это поле остаётся центральным элементом, а календарь рассматривается как вспомогательный интерфейс.
Визуальный календарь строится как таблица дат, однако для программ чтения экрана требуется строгая ARIA-структура:
role="dialog" — контейнер календаряaria-modal="true" — фиксация модального поведенияrole="grid" — календарная сеткаrole="gridcell" — отдельная датаaria-selected="true" — выбранная датаaria-disabled="true" — недоступные датыСтруктурный пример:
<div class="flatpickr-calendar" role="dialog" aria-modal="true">
<div class="dayContainer" role="grid">
<div role="row">
<span role="gridcell" aria-selected="false">1</span>
<span role="gridcell" aria-selected="true">2</span>
</div>
</div>
</div>
Для VoiceOver и NVDA критично, чтобы каждая ячейка имела корректное текстовое представление: день недели, число и состояние (выбран/недоступен).
Календарь должен управлять фокусом предсказуемо:
В JAWS некорректный фокус приводит к «потере контекста», когда пользователь оказывается вне календарной сетки без понимания текущей позиции.
Типичная логика:
onOpen: function(selectedDates, dateStr, instance) {
instance.calendarContainer.setAttribute("role", "dialog");
instance.calendarContainer.setAttribute("aria-modal", "true");
instance.jumpToDate(selectedDates[0] || new Date());
}
Ключевой момент — активная дата должна всегда иметь фокус или быть
программно обозначенной через tabindex="0".
Программы чтения экрана требуют динамического оповещения о смене
состояния календаря. Для этого используется aria-live.
Типовые сценарии:
Рекомендуемая структура:
<div id="fp_live_region" aria-live="polite" aria-atomic="true"></div>
При изменении месяца:
function announceMonth(monthName) {
document.getElementById("fp_live_region").textContent =
"Текущий месяц: " + monthName;
}
NVDA и VoiceOver интерпретируют такие изменения как отдельные сообщения, не прерывая текущую озвучку.
Кнопки переключения месяца должны быть однозначно идентифицируемыми:
Недопустимо использование только визуальных иконок без текстовой метки.
Корректный вариант:
<button aria-label="Предыдущий месяц">‹</button>
<button aria-label="Следующий месяц">›</button>
При использовании JAWS без aria-label кнопка будет
озвучена как «button», что делает навигацию непрозрачной.
Хотя программы чтения экрана обеспечивают озвучивание, управление остаётся клавиатурным. Поэтому необходимо соблюдать единый набор действий:
Важно, чтобы активная дата всегда имела:
onKeyDown: function(_, __, ___, e) {
if (e.key === "ArrowRight") {
this.changeDate(1);
}
}
VoiceOver синхронизирует фокус с визуальным выделением, поэтому рассинхронизация приводит к некорректному озвучиванию.
После выбора даты необходимо обеспечить двойное представление:
Пример:
onChange: function(selectedDates, dateStr, instance) {
instance.input.value = dateStr;
document.getElementById("fp_live_region").textContent =
"Выбрана дата: " + dateStr;
}
В NVDA это позволяет мгновенно подтвердить действие без необходимости повторного наведения на поле.
Отключённые даты должны быть явно помечены:
aria-disabled="true"<span role="gridcell" aria-disabled="true">13</span>
JAWS корректно сообщает такие элементы как «disabled», однако только
при наличии атрибута aria-disabled.
Формат даты влияет на то, как она озвучивается. Например:
VoiceOver использует системную локаль, поэтому важно синхронизировать:
locale в FlatpickrПри режимах multiple и range требуется расширенная семантика:
Пример объявления диапазона:
onChange: function(selectedDates, dateStr) {
const msg = selectedDates.length === 2
? "Диапазон выбран: " + dateStr
: "Выбрана дата: " + dateStr;
document.getElementById("fp_live_region").textContent = msg;
}
NVDA воспринимает такие сообщения как отдельные события, что позволяет пользователю отслеживать логику выбора.
aria-label у кнопок навигацииJAWS особенно чувствителен к потере фокуса и может «выпадать» из календаря при неправильной обработке DOM-обновлений.
Ключевой принцип — синхронизация трёх слоёв:
В Flatpickr эта синхронизация достигается через обработчики событий изменения состояния и корректное управление атрибутами gridcell.
Любое рассогласование приводит к ситуации, когда экранный диктор озвучивает одно состояние, а пользователь видит другое, что критично для доступности интерфейса.