Первая теория: плагин работает некорректно
Что мы проверили:
Плагин не оборачивает правила? Нет, всё ок. После сборки
hover
был в медиазапросах.Не работает
hover: hover?
Проверили в эмуляции мобильных устройств — работает.
Значит, проблема не в плагине.
Вторая теория: баг на отдельных устройствах
Мы начали тестировать разные модели через Browserstack. На большинстве устройств всё нормально, но есть одно но.
Samsung Galaxy игнорирует всё.
Ему всё равно, что мы оборачиваем hover
в @media (hover: hover)
. Он отвечает: «Да-да, я поддерживаю hover
», а потом предательски залипает.
Дальнейшая проверка показала, что дело не только в Galaxy. Похожая история случается и на некоторых моделях Vivo.

В процессе поиска ответов мы наткнулись на интересную статью о том, как определять устройства с тачскрином на чистом CSS. Такой способ удобен и для SSR, поэтому мы его изучили. Были варианты и с хуками, но они работают только на клиенте.
Как проверить вашу мобилку
Если интересно, вот ссылки, чтобы прогнать ваши устройства:
Глубже в проблему: откуда берётся true?
Возникает логичный вопрос: как браузер определяет, что событие hover поддерживается?
Мы обратились к исходникам Chromium. Там есть файл event.cc, где перечислены все события. В нём видно: браузер получает события от ОС.
А в файле platform_event.h можно заметить, что тип события зависит от платформы (Windows, Mac, iOS, Linux).
То есть:
Аппаратная часть устройства сообщает ОС, что за событие произошло.
ОС передаёт его браузеру через драйверы и обработчики.
Браузер преобразует его в знакомые нам события, с которыми мы работаем.
Отсюда и ответ: если устройство сообщает, что hover
есть, браузер возвращает true
на window.matchMedia(’(hover: hover)’).
MDN и медиазапрос pointer
На MDN мы нашли медиазапрос pointer. Он определяет, какое устройство ввода используется:
fine
— точное устройство (мышь, тачпад, стилус).coarse
— неточное (сенсорный экран).
Проверить это можно так:
window.matchMedia('(pointer: coarse)');
window.matchMedia(’(pointer: fine)’).
Метод возвращает объект с полем matches
, где и видно, какое устройство ввода сейчас активно.
Почему pointer: fine недостаточно
На первый взгляд кажется: давайте использовать только pointer: fine
и забудем про hover. Но не всё так просто.
У некоторых устройств
pointer: fine
возвращаетtrue
, ноhover
они не поддерживают.Стилусы иногда определяются как
fine
, но тоже не имеют hover.
Поэтому мы должны проверять оба условия:
@media (hover: hover) and (pointer: fine) { ... }
А как же спецификация?

Если обратиться к спеке W3C, то там есть важное уточнение. Даже если устройство имеет pointer: coarse
, это не значит, что точные клики невозможны. Это говорит о том, что мы можем совершать точные клики при помощи того же стилуса.
Иными словами: медиазапросы дают разработчику лишь подсказку о том, как лучше строить интерфейс, а не жёсткую гарантию.
Решение:
Мы пришли к следующему варианту: используем медиазапрос (hover: hover) and (pointer: fine)
, чтобы hover-стили применялись только на устройствах с мышью или тачпадом.
Варианты внедрения
Обернуть стили вручную. Подходит для небольших проектов.
Допилить оригинальный плагин.
Написать свой.
Так как в репозитории оригинального плагина давно висят нерешённые реквесты, мы решили не ждать и сделать свой.
Наш плагин
Когда стало ясно, что простого hover: hover
недостаточно, мы решили создать свой плагин.
Чтобы сделать использование плагина удобнее и безопаснее, мы сделали версию на TypeScript.
Это дало:
Чёткую типизацию входных и выходных данных;
Упрощённое отлавливание ошибок на этапе разработки;
Более структурный и понятный код для будущих изменений.
Теперь у нас есть «боевая» версия плагина, которую можно смело подключать в проект и не переживать за hover
на мобильных устройствах.
Итог
История с залипающим hover показала: полагаться только на hover: hover не стоит — иногда он «залипает» и слегка раздражает пользователя, хотя интерфейс при этом и не ломается.
С нашим решением hover остаётся только там, где он действительно нужен, а «притворщики» типа тачскринов больше не вмешиваются.
В результате мы получили полезный и интересный опыт в автоматическом оборачивании hover в медиазапросы с более полным условием. Залипающий hover больше не раздражает, а ручное прописывание условий стало не нашей головной болью — так мы (а теперь и вы) можем чуть расслабиться и довериться автоматике.