Дайджесты новостей
Макросъемка смартфона с быстрым нажатием кнопки плюс и наглядным отключением паразитного зума через CSS.

Избавляемся от паразитного зума при частых кликах на кнопки: магия CSS touch-action

Представь типичную бытовую сцену. Человек заходит с мобильного телефона в интернет-магазин, находит нужный товар и хочет добавить в корзину сразу пять упаковок. Он быстро делает серию коротких тапов по кнопке с плюсом: тап-тап-тап-тап. И вместо того чтобы получить пять единиц товара, экран смартфона внезапно дергается, увеличивается в два раза и съезжает куда-то в правый угол.

Что произошло? Мобильный браузер (будь то Safari на iOS или Chrome на Android) добросовестно решил, что пользователь совершил жест двойного тапа (double-tap to zoom), предназначенный для чтения мелкого текста на неадаптированных сайтах. Пользователь раздражен, логика добавления сбита, а конверсия корзины падает.

Почему user-scalable=no — это опасный антипаттерн

В старые времена фронтенд-разработчики «лечили» эту головную боль радикальным и очень грубым способом. В шапку страницы прописывали метатег вьюпорта с запретом любого масштабирования:

<!-- Категорически устаревший и вредный подход! -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

Никогда так не делай. Во-первых, это прямое нарушение критериев доступности WCAG 2.1 (пункт 1.4.4 Resize Text). Слабовидящие пользователи физически теряют возможность приблизить мелкий шрифт или рассмотреть детали фотографии. Во-вторых, современные мобильные браузеры (включая WebKit в iOS) уже давно научились намеренно игнорировать директиву user-scalable=no, защищая базовые права пользователя на масштабирование страницы жестом щипка (pinch-to-zoom).

Точечное решение: touch-action: manipulation

Цивилизованный и на 100% соответствующий веб-стандартам W3C способ описал фронтенд-инженер Энди Белл (Piccalilli). Речь идет о стандартном CSS-свойстве touch-action, которое управляет тем, как браузер перехватывает сенсорные жесты до передачи их в JavaScript.

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

/* Базовый интерактивный класс для кнопок счетчиков и контролов плеера */
.btn-stepper {
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
}

Смотри, что здесь происходит. Значение manipulation является стандартизированным синонимом для комбинации pan-x pan-y pinch-zoom. Браузер отключает исключительно распознавание двойного тапа на самой кнопке, но при этом оставляет полностью рабочей горизонтальную и вертикальную прокрутку страницы, а также сохраняет масштабирование двумя пальцами (pinch-to-zoom).

Давай взглянем на аккуратную разметку компонента счетчика товаров в корзине:

<div class="cart-stepper" role="group" aria-label="Количество товара">
  <button type="button" class="btn-stepper" aria-label="Уменьшить количество">-</button>
  <span class="stepper-value" aria-live="polite">3</span>
  <button type="button" class="btn-stepper" aria-label="Увеличить количество">+</button>
</div>

В связке с правилом user-select: none кнопка перестает выделяться синей рамкой при быстрых кликах, а интерфейс начинает ощущаться быстрым и монолитным, как нативное мобильное приложение. Если ты интересуешься современными трюками декларативного CSS, рекомендуем также вспомнить наш разбор того, как работает якорное позиционирование, где чистое CSS-декларирование заменяет десятки строк JavaScript.

Где таится скрытый подвох

Единственное важное предостережение: никогда не вешай touch-action: manipulation глобально на селектор * или тег body. Если навесить это правило на всю страницу целиком, пользователи потеряют удобную возможность быстро приблизить блок текста двойным касанием на экранах смартфонов.

Используй свойство точечно: на кнопках аудиоплееров («следующий трек»), игровых контролах, кнопках калькуляторов и шаговых счетчиках корзины — и мобильный интерфейс станет удобным без единой строчки костылей.