На JavaScript/HTML/CSS: даны формы с автозаполнением и нестандартным поведением на мобильных браузерах — как диагностировать и исправить проблемы адаптивности и доступности (accessibility) при разных UA и вспомогательных технологиях
Коротко, по делу: как диагностировать и фиксить проблемы адаптивности и доступности форм с автозаполнением и нестандартным поведением на мобильных браузерах. 1) Диагностика — что и как тестировать - Тестировать на реальных устройствах плюс эмуляторы/BrowserStack. - Инструменты: Chrome DevTools (remote debugging Android), Safari Web Inspector (iOS), Lighthouse, axe-core, WAVE, Accessibility Inspector (macOS/iOS). - Прогонять с разными UA/браузерами: iOS Safari, Chrome Android, Samsung Internet, Firefox Mobile — и с/без менеджеров паролей. - Тесты: клавиатурная навигация, голосовые скринридеры (VoiceOver, TalkBack), увеличение/зум, смена ориентации, открытие виртуальной клавиатуры, автозаполнение, поведение фокуса, контраст и видимость фокуса. - Логирование событий: отслеживайте focus/blur/input/change/animationstart, т.к. автозаполнение часто не триггерит `input`/`change`. 2) Общие причины проблем и быстрые исправления - Отсутствует корректный meta viewport:
- iOS автозум вызывает масштаб при шрифтах меньше ...16...16...16px — используйте минимум ...16...16...16px для интерактивного текста. - Поля скрываются виртуальной клавиатурой из‑за фиксированных футеров; не используйте жестко pinned элементы без учёта resize. - 100vh ведёт себя некорректно на мобилах (ошибка из‑за интерфейса браузера) — использовать динамическую переменную высоты. - Автозаполнение браузера/менеджеров паролей может менять стили (желтый/синий фон), не вызывать события, менять значение до JS-сценариев. 3) Практические исправления (код/паттерны) a) Viewport / шрифты
b) Динамическая высота вместо 100vh const setVh = () => { document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`); }; setVh(); window.addEventListener('resize', setVh); /* CSS: height: calc(var(--vh) * 100); */ c) Переопределение автозаполнения (WebKit) input:-webkit-autofill { -webkit-text-fill-color: #000 !important; -webkit-box-shadow: 0 0 0px 1000px white inset !important; box-shadow: 0 0 0px 1000px white inset !important; transition: background-color 5000s ease-in-out 0s; /* чтоб не мигало */ } d) Форсировать repaint после автозаполнения (триггер по animationstart) @keyframes onAutoFillStart { from {} to {} } input:-webkit-autofill { animation-name: onAutoFillStart; animation-duration: 0.01s; } document.addEventListener('animationstart', (e) => { if (e.animationName === 'onAutoFillStart') { /* обновить состояние, валидацию */ } }); e) Правильные атрибуты для автозаполнения и клавиатуры
f) Доступность (labels, aria) EmailМы никогда не передаём email. - Не использовать placeholder вместо label. - Для ошибок: Ошибка и при ошибке ставьте focus на первое поле с ошибкой. - Для динамических форм — управлять фокусом и сообщать изменения через aria-live. g) Фокус и видимость - Никогда не убирайте outline без замены: :focus { outline: 2px solid #005fcc; outline-offset: 2px; } - Обеспечьте правильный tabindex (обычно оставлять без явного tabindex, использовать порядок DOM). h) Сенсорные цели и контраст - Минимальная целевая область: ...44...44...44px × ...44...44...44px. - Контраст текста >= WCAG требования (AA/AAA) — тестировать контраст. 4) Специфические поведенческие баги и решения - Автозаполнение не триггерит JS-валидацию: после загрузки/видимости формы вручную проверяйте значения полей (e.g. run validate on DOMContentLoaded и на animationstart автозаполнения). - Password managers на iOS могут вставлять значения в неявные поля — использовать корректные autocomplete/name и видимые поля с нормальным DOM-порядком. - Поля, появляющиеся динамически: если добавляете поля после загрузки, некоторые авто‑филлы не сработают — добавляйте серверный hint (autocomplete) и, при необходимости, реинициализируйте. 5) Тесты с ассистивными технологиями - Прогонять сценарии с VoiceOver и TalkBack: навигация по полям, чтение лейблов/ошибок, aria-live. - Проверять порядок навигации Tab/Shift+Tab. - Проверять работу с экранной клавиатурой и появление/скрытие элементов. 6) Полезные чек‑листы - Есть label для каждого поля? Да/Нет. - Указан правильный autocomplete? Да/Нет. - inputmode для числовых/тел полей? Да/Нет. - Фокус видим? Да/Нет. - Поля не перекрываются клавиатурой (использовать resize/scrollIntoView при фокусе)? Да/Нет. - Не отключён зум (не ставьте maximum-scale=1) — уважайте доступность. 7) Ресурсы для глубокого поиска - axe-core, Lighthouse, WAI-ARIA Authoring Practices, WebKit autofill docs, MDN autocomplete tokens. Заключение (в 1 предложении): тестируйте на реальных устройствах + автоматических инструментах, используйте корректные атрибуты (autocomplete, inputmode), исправляйте стили автозаполнения через :-webkit-autofill и динамически учитывайте высоту экрана/клавиатуру, а также обеспечьте корректные лейблы и управление фокусом для доступности.
1) Диагностика — что и как тестировать
- Тестировать на реальных устройствах плюс эмуляторы/BrowserStack.
- Инструменты: Chrome DevTools (remote debugging Android), Safari Web Inspector (iOS), Lighthouse, axe-core, WAVE, Accessibility Inspector (macOS/iOS).
- Прогонять с разными UA/браузерами: iOS Safari, Chrome Android, Samsung Internet, Firefox Mobile — и с/без менеджеров паролей.
- Тесты: клавиатурная навигация, голосовые скринридеры (VoiceOver, TalkBack), увеличение/зум, смена ориентации, открытие виртуальной клавиатуры, автозаполнение, поведение фокуса, контраст и видимость фокуса.
- Логирование событий: отслеживайте focus/blur/input/change/animationstart, т.к. автозаполнение часто не триггерит `input`/`change`.
2) Общие причины проблем и быстрые исправления
- Отсутствует корректный meta viewport:
- iOS автозум вызывает масштаб при шрифтах меньше ...16...16...16px — используйте минимум ...16...16...16px для интерактивного текста.
- Поля скрываются виртуальной клавиатурой из‑за фиксированных футеров; не используйте жестко pinned элементы без учёта resize.
- 100vh ведёт себя некорректно на мобилах (ошибка из‑за интерфейса браузера) — использовать динамическую переменную высоты.
- Автозаполнение браузера/менеджеров паролей может менять стили (желтый/синий фон), не вызывать события, менять значение до JS-сценариев.
3) Практические исправления (код/паттерны)
a) Viewport / шрифты
b) Динамическая высота вместо 100vh
const setVh = () => {
document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`);
};
setVh();
window.addEventListener('resize', setVh);
/* CSS: height: calc(var(--vh) * 100); */
c) Переопределение автозаполнения (WebKit)
input:-webkit-autofill {
-webkit-text-fill-color: #000 !important;
-webkit-box-shadow: 0 0 0px 1000px white inset !important;
box-shadow: 0 0 0px 1000px white inset !important;
transition: background-color 5000s ease-in-out 0s; /* чтоб не мигало */
}
d) Форсировать repaint после автозаполнения (триггер по animationstart)
@keyframes onAutoFillStart { from {} to {} }
input:-webkit-autofill { animation-name: onAutoFillStart; animation-duration: 0.01s; }
document.addEventListener('animationstart', (e) => {
if (e.animationName === 'onAutoFillStart') { /* обновить состояние, валидацию */ }
});
e) Правильные атрибуты для автозаполнения и клавиатуры
f) Доступность (labels, aria)
EmailМы никогда не передаём email.
- Не использовать placeholder вместо label.
- Для ошибок: Ошибка и при ошибке ставьте focus на первое поле с ошибкой.
- Для динамических форм — управлять фокусом и сообщать изменения через aria-live.
g) Фокус и видимость
- Никогда не убирайте outline без замены: :focus { outline: 2px solid #005fcc; outline-offset: 2px; }
- Обеспечьте правильный tabindex (обычно оставлять без явного tabindex, использовать порядок DOM).
h) Сенсорные цели и контраст
- Минимальная целевая область: ...44...44...44px × ...44...44...44px.
- Контраст текста >= WCAG требования (AA/AAA) — тестировать контраст.
4) Специфические поведенческие баги и решения
- Автозаполнение не триггерит JS-валидацию: после загрузки/видимости формы вручную проверяйте значения полей (e.g. run validate on DOMContentLoaded и на animationstart автозаполнения).
- Password managers на iOS могут вставлять значения в неявные поля — использовать корректные autocomplete/name и видимые поля с нормальным DOM-порядком.
- Поля, появляющиеся динамически: если добавляете поля после загрузки, некоторые авто‑филлы не сработают — добавляйте серверный hint (autocomplete) и, при необходимости, реинициализируйте.
5) Тесты с ассистивными технологиями
- Прогонять сценарии с VoiceOver и TalkBack: навигация по полям, чтение лейблов/ошибок, aria-live.
- Проверять порядок навигации Tab/Shift+Tab.
- Проверять работу с экранной клавиатурой и появление/скрытие элементов.
6) Полезные чек‑листы
- Есть label для каждого поля? Да/Нет.
- Указан правильный autocomplete? Да/Нет.
- inputmode для числовых/тел полей? Да/Нет.
- Фокус видим? Да/Нет.
- Поля не перекрываются клавиатурой (использовать resize/scrollIntoView при фокусе)? Да/Нет.
- Не отключён зум (не ставьте maximum-scale=1) — уважайте доступность.
7) Ресурсы для глубокого поиска
- axe-core, Lighthouse, WAI-ARIA Authoring Practices, WebKit autofill docs, MDN autocomplete tokens.
Заключение (в 1 предложении): тестируйте на реальных устройствах + автоматических инструментах, используйте корректные атрибуты (autocomplete, inputmode), исправляйте стили автозаполнения через :-webkit-autofill и динамически учитывайте высоту экрана/клавиатуру, а также обеспечьте корректные лейблы и управление фокусом для доступности.
Основные проблемы, которые обычно возникают
Мобильные браузеры создают типичные баги:
Автозаполнение
браузер игнорирует autocomplete="off"
Safari автозаполняет даже скрытые поля
Chrome подставляет неправильные данные (email вместо имени и т.п.)
Менеджеры паролей изменяют структуру DOM
Адаптивность
неверные viewport-мета теги
залипающая экранная клавиатура (особенно Safari)
input не прокручивается выше клавиатуры
CSS-блокировки зумирования ломают доступность
Доступность
неправильные label
отсутствуют aria-*
iOS пропускает чтение некоторых placeholder
неверный порядок фокуса
кастомные UI-компоненты (селекты, свитчи, radio) недоступны
---
🧪 2. Инструменты диагностики
2.1. Desktop-инструменты (для мобильных UA)
Chrome DevTools → Device Toolbar
эмуляция User-Agent
эмуляция экранов и DPR
эмуляция сенсорных событий
эмуляция медленных CPU/сетей
Но: на 100% поведение мобильных браузеров не повторяется.
---
Safari Web Inspector (iOS)
Лучший способ отладки iPhone / iPad.
Позволяет:
смотреть реальные layout shifts после появления клавиатуры
видеть реальные inputmode/autofill реакции
слушать accessibility tree VoiceOver
---
Android → Chrome DevTools Remote Debugging
физическое устройство даёт реальное поведение
можно смотреть события клавиатуры и autofill
---
2.2. Инструменты доступности
Chrome Lighthouse → Accessibility audit
Axe DevTools (плагин)
Accessibility Tree viewer (Chrome/Safari)
Позволяют найти:
дублирующиеся id
неправильные label
игнорируемые элементы
---
2.3. Диагностика вспомогательных технологий
Тесты на реальных устройствах:
iOS (без вариантов):
Включить Settings → Accessibility → VoiceOver
Проверить Gesture Navigation
Проверить чтение полей формы: label → description → error → hint
Android:
Settings → Accessibility → TalkBack
---
🎯 3. Как правильно настроить формы: best practices
3.1. HTML: правильные autocomplete атрибуты
Используйте строгие значения, иначе автозаполнение не сработает:
<input type="text" name="given-name" autocomplete="given-name">
<input type="text" name="family-name" autocomplete="family-name">
<input type="email" name="email" autocomplete="email">
<input type="tel" name="tel" autocomplete="tel">
<input type="password" name="new-password" autocomplete="new-password">
<input type="password" name="current-password" autocomplete="current-password">
❌ Ошибки, вызывающие баги:
autocomplete="off"
autocomplete="nope"
неправильные name
---
3.2. HTML: правильные label и aria
Правильно:
<label for="email">Email</label>
<input id="email" aria-describedby="emailHelp" autocomplete="email">
<div id="emailHelp">Мы не передаём данные третьим лицам</div>
Неправильно (частая ошибка):
<input placeholder="Email">
Почему?
placeholder читает VoiceOver не всегда
Android TalkBack игнорирует placeholder как label
---
3.3. CSS: адаптивность без багов
Обязательно:
<meta name="viewport" content="width=device-width, initial-scale=1">
Запрещено:
<meta name="viewport" content="user-scalable=no">
→ ломает доступность (невозможность увеличения текста)
---
Избегайте 100vh на мобильных
Мобильные браузеры меняют высоту в зависимости от клавиатуры.
Используйте:
height: 100dvh;
или
min-height: 100%;
---
Починить «прыжки» при автозаполнении
Safari увеличивает шрифт < 16px.
Используйте:
input {
font-size: 16px;
}
---
3.4. JS: детектирование клавиатуры
Клавиатура изменяет viewport — можно слушать:
iOS 15+
window.visualViewport.addEventListener("resize", () => {
console.log("keyboard opened/closed", window.visualViewport.height);
});
Android:
window.addEventListener("resize", () => {
// проверка difference > 150px
});
---
🔍 4. Диагностика автозаполнения
4.1. Проверка во всех режимах
Нужно протестировать:
Платформа Browser Autofill Password manager
Android Chrome Google Autofill Bitwarden / 1Password
iOS Safari Apple Autofill iCloud Keychain
Тест-кейсы:
1. Первый клик по полю
2. Второй клик
3. Поля email/password
4. Поля «Имя / Фамилия»
5. Скрытые поля → должны игнорироваться
6. Кастомные inputs внутри Shadow DOM
7. Кастомный select
---
🧩 5. Диагностика доступности (по шагам)
Шаг 1: Проверить структуру:
Chrome DevTools → Elements → Accessibility → Name, Role, Value
Шаг 2: Проверить порядок фокуса
DevTools → Rendering → Emulate Focus
Шаг 3: Проверить читабельность VoiceOver/TalkBack
Шаг 4: Проверить ошибки и подсказки
Лучший паттерн:
<label for="input">Имя</label>
<input id="input" aria-invalid="true" aria-describedby="err">
<div id="err" role="alert">Имя обязательно</div>