移动端:hover是触摸模拟而非悬停,首次touchstart触发后不自动清除,需@media (hover: hover) and (pointer: fine)精准控制,并用:active替代实现可靠反馈。

因为浏览器把首次触摸当成:hover起点,但没有“手指移开”事件来清除它——这不是 Tailwind 的问题,是 iOS Safari 和部分 Android WebView 的标准行为。
移动端:hover根本不是“悬停”,而是“一次性的模拟触发”
触摸设备没有鼠标指针,:hover在移动端本质是兼容性补丁:第一次 touchstart 会短暂激活 :hover,之后就不再响应;更麻烦的是,某些内核(比如 iOS Safari)不会在 touchend 时自动退出该状态,导致样式卡住不退。
- 点一下按钮,
hover:bg-blue-500一直挂着,大概率就是这个原因 -
<button></button>比<div> 更容易卡住,因为原生控件自带高亮逻辑,和 CSS <code>:hover并行运行 - 微信/QQ 内置浏览器(X5 内核)甚至完全忽略
:hover状态更新,残留更顽固 - 写法必须是:
@media (hover: hover) and (pointer: fine),不能漏掉and (pointer: fine) - 这个组合只对鼠标、触控板、触控笔生效,自动过滤手指点击
-
transition属性也得包进同一媒体查询里,否则移动 Safari 可能解析到但不触发动画 - 规则必须放在常规样式之后,否则会被普通
.btn:hover覆盖 - 最简做法:给
加ontouchstart="",全局激活:active - 或者确保元素是
<button></button>,或带role="button"且有tabindex="0" -
:active动画持续时间建议 ≤0.15s,避免点击后延迟感明显 - 别用
!important强行覆盖:hover,而是让:active自身定义完整状态(背景、缩放、边框等)
@media (hover: hover) and (pointer: fine) 才是唯一靠谱的守门员
单独用 @media (hover: hover) 在 iPad 或 Surface 上恒为 true,毫无区分度;必须叠加精度判断,才能排除所有手指触控场景。
别依赖:hover做关键交互,改用:active + 显式可交互标记
:active 是移动端最稳的瞬时反馈替代方案,但它在 iOS Safari 默认被禁用,除非满足明确条件。
真正难处理的不是怎么写 hover,而是怎么让开发者意识到:移动端根本没有“悬停”这个交互维度。一旦默认把它当 PC 行为去调试,就会陷入无限 patch 的循环。











