При использовании Cleave.js основная сложность с точки зрения доступности возникает из-за того, что пользователь вводит данные в поле, которое визуально и фактически изменяется на лету: добавляются разделители, форматируются числа, перестраиваются блоки ввода даты, номера карт, телефонов и других структурированных значений. Это поведение удобно для визуального восприятия, но может конфликтовать с ожиданиями вспомогательных технологий, прежде всего экранных дикторов.
ARIA-атрибуты (Accessible Rich Internet Applications) используются для того, чтобы восстановить семантическую ясность там, где DOM-структура или поведение JavaScript её нарушают или усложняют интерпретацию. В контексте Cleave.js они выполняют не декоративную, а компенсаторную функцию: объясняют, что именно происходит с вводом, и как это должно восприниматься программами чтения экрана.
Обычное HTML-поле ввода уже обладает базовой семантикой:
<input type="text"> воспринимается как текстовое
поле<input type="tel"> — как телефонный ввод<input type="number"> — как числовое полеЭкранные дикторы используют эти сигналы для формирования поведения озвучивания. Однако при подключении Cleave.js стандартная семантика перестаёт полностью соответствовать реальности:
Это создаёт разрыв между «что говорит DOM» и «что видит пользователь».
Cleave.js не меняет значение через замену input на кастомный
компонент, он работает поверх стандартного <input>.
Однако даже при таком подходе возникают ключевые проблемы:
Например, пользователь вводит:
1234567890
А поле отображает:
1 234 567 890
Для экранного диктора это может звучать либо как:
Без дополнительной информации пользователь вспомогательных технологий не понимает:
Cleave.js обновляет value на каждом вводе, что может приводить к:
Используется для явного описания назначения поля, когда визуальная подпись недостаточна или отделена от input.
Пример смысловой нагрузки:
Важно: aria-label не должен дублировать визуальный label
без необходимости. Если <label> уже связан через
for, предпочтительнее использовать именно его.
Более корректный вариант, чем aria-label, если в
интерфейсе уже существует визуальная подпись.
<label id="phone-label">Телефон</label>
<input id="phone" aria-labelledby="phone-label">
В контексте Cleave.js это сохраняет семантику, не вмешиваясь в поведение форматирования.
Используется для пояснения формата ввода, особенно важно при масках.
Пример:
<input id="card" aria-describedby="card-help">
<div id="card-help">Введите номер карты без пробелов, они будут добавлены автоматически</div>
Однако при использовании Cleave.js формулировка должна учитывать обратную ситуацию: пользователь вводит без разделителей, но видит форматированный результат.
Более точное описание:
Cleave.js работает на основе конфигураций (например,
creditCard, date, phone,
numeral). Эти режимы напрямую влияют на семантику поля, но
не сообщают об этом экранному диктору.
При использовании телефонной маски:
new Cleave(input, {
phone: true,
phoneRegionCode: 'RU'
});
С точки зрения доступности важно указать:
ARIA:
<input
aria-label="Телефон"
aria-describedby="phone-format"
/>
<div id="phone-format">
Форматирование номера выполняется автоматически
</div>
Наиболее чувствительный сценарий, так как формат строго структурирован:
new Cleave(input, {
creditCard: true
});
Проблема заключается в том, что пользователь слышит набор цифр, а визуально видит блоки по 4 цифры.
Рекомендуемая семантика:
<input
aria-label="Номер банковской карты"
aria-describedby="card-format"
/>
<div id="card-format">
Номер вводится непрерывно, разделение на группы выполняется автоматически
</div>
Дата — один из самых сложных случаев, поскольку формат может быть неоднозначным:
Cleave.js может форматировать ввод, но не фиксирует смысловую интерпретацию.
new Cleave(input, {
date: true,
datePattern: ['d', 'm', 'Y']
});
Семантически важно явно фиксировать порядок:
<input
aria-label="Дата рождения (день, месяц, год)"
aria-describedby="date-format"
/>
<div id="date-format">
Формат: день, месяц, год. Разделители добавляются автоматически
</div>
Cleave.js изменяет значение input в реальном времени. Это может
конфликтовать с поведением экранных дикторов, которые реагируют на
input-события.
Использование aria-live в данном контексте обычно не
рекомендуется внутри самого input, но может быть полезно для внешних
индикаторов состояния.
Например:
<div id="status" aria-live="polite"></div>
И обновление только ключевых состояний:
Важно: не дублировать каждое изменение символа.
Cleave.js не является валидатором, но часто используется совместно с проверкой корректности формата.
Применяется при ошибке:
<input aria-invalid="true">
или:
<input aria-invalid="false">
При проверке кредитной карты:
При ошибке:
<input
aria-invalid="true"
aria-describedby="card-error"
/>
<div id="card-error">Номер карты некорректен</div>
Хотя ARIA напрямую не управляет курсором, семантика влияет на восприятие поведения поля.
Особенность Cleave.js:
Для пользователя экранного диктора это может восприниматься как «нестабильное поле ввода».
Семантическое решение не техническое, а описательное:
Несмотря на наличие ARIA, <label> остаётся
основным механизмом семантики.
Корректная структура:
<label for="input">Номер телефона</label>
<input id="input">
ARIA применяется только дополнительно, а не вместо.
В связке с Cleave.js label особенно важен, потому что:
Поле выглядит как обычное, но ведёт себя иначе.
Одновременное использование:
<label>aria-labelбез необходимости создаёт конфликт приоритетов.
Приводит к постоянным озвучиваниям при каждом вводе символа.
Фокус только на визуальном формате без объяснения логики ввода.
Cleave.js улучшает UX визуально, но требует компенсирующего слоя семантики. ARIA в этом контексте не усиливает интерфейс, а восстанавливает утраченный смысл: