Files
bare/docs/decisions/076-ios-input-accessory-is-system.md

6.7 KiB
Raw Permalink Blame History

ADR-076: Панель ввода iOS системная, строка сообщения — textarea

Пересматривает ADR-064, ADR-069 и ADR-071. Уточняет docs/ui.md, «Чат» и «Доступность».

Контекст

В v1.1 строка сообщения прошла два варианта: textarea вынесли из <form>, затем заменили на contenteditable="plaintext-only". Оба решения исходили из предположения, что системная полоса iOS со стрелками и «готово» появляется только у поля формы.

Проверка v1.1 на реальном iPhone опровергла предположение: полоса осталась и у contenteditable. Это часть клавиатурного интерфейса WebKit, а не узел страницы. Web API для её отключения нет. В нативной оболочке внешний вид клавиатуры может менять хост iOS; установленная PWA остаётся веб-страницей и такого доступа не получает.

На том же экране часть потерянного места оказалась не системной панелью: строка ввода всегда добавляла 20px + env(safe-area-inset-bottom). Safari сохраняет нижний safe-area домашнего индикатора и после появления клавиатуры, хотя её прямоугольная поверхность уже закрывает этот край. Получалась отдельная пустая полоса примерно той же высоты, что системная панель.

contenteditable при этом стоил клиенту ручной реализации подсказки, предела, вставки без разметки, переноса строки, disabled, счётчика, работы IME и запасного пути для браузеров без plaintext-only. Главное следствие ADR-069 не наступило, а цена осталась.

Решение

  • Строка сообщения снова становится textarea с нативными placeholder, maxLength, disabled, выделением, вставкой и отменой. Число rows считается по фактической высоте текста от одной до семи: это сохраняет автовысоту в Safari без поддержки field-sizing. Доступное имя повторяет подсказку через aria-label, кнопка > объявляется как «отправить»; autocapitalize, autocorrect и enterkeyhint остаются прежними.
  • Контейнер div role="form" и кнопка type="button" остаются. Они не убирают системную панель, но и не создают отдельного поведения; менять их ещё раз без причины незачем.
  • Системная панель iOS принимается как ограничение веб-клиента. inputmode="none" и собственная экранная клавиатура не используются: убрать вместе с панелью системную клавиатуру означает потерять обычный набор, раскладки, диктовку и доступность.
  • Нативная iOS-оболочка не добавляется в Bare. Если абсолютное управление панелью станет требованием, это отдельный продуктовый и архитектурный выбор.
  • Тап по неинтерактивному месту истории вызывает blur() строки сообщения и закрывает клавиатуру. Кнопки и ссылки в ленте сохраняют своё действие.
  • Пока поле сфокусировано и visualViewport уменьшен как при экранной клавиатуре, весь обычный нижний отступ строки ввода (20px + safe-area на узком экране, 28px + safe-area на широком) заменяется на 6 px. Эвристика сверяет масштаб и ширину и требует существенного уменьшения высоты; один фокус не считается клавиатурой, поэтому с физической клавиатурой обычный отступ остаётся. При закрытии размеры пересчитываются. Смена ширины после фокуса и уход приложения в фон возвращают полный безопасный отступ до следующего тапа по полю: известное старое значение visualViewport после поворота или возврата не должно прижать ввод к домашнему индикатору.

Следствия

  • Строка сообщения снова опирается на браузерное текстовое поле и стала заметно проще. Лишнее не проходит через maxLength; ручная обработка beforeinput, вставки, IME и текстовых узлов удалена.
  • Полоса со стрелками и «готово» на iPhone остаётся. Код и документация больше не обещают веб-странице контроль над системным UI.
  • Пустой зазор между строкой ввода и системной панелью исчезает; место возвращается истории без вмешательства в клавиатуру.
  • Тап по истории возвращает место ленте без нажатия системной кнопки «готово».
  • Проверка самой панели и точного момента перестроения visualViewport по-прежнему требует реального iPhone в Safari и установленном PWA; поведение textarea, отправки, эвристики размеров и снятия фокуса проверяется в обычном браузере.