正确写法是@media (hover: hover) and (pointer: fine),它要求设备主输入机制既支持悬停又属精细类型(如鼠标、触控笔),从而精准排除手指触摸等粗粒度场景,避免surface、s22等设备因误判hover导致:hover样式“卡住”。

为什么 @media (hover: hover) 单独用会误判 Surface、三星 S22 等设备
Surface Pro、三星 Galaxy S22 这类设备报告同时支持 hover: hover 和 pointer: coarse,浏览器照单全收,导致你写的 .menu:hover { display: block; } 在手指点一下后“卡住”展开,再点别处也不收 —— 因为它把首次触摸当作了 hover 入口,但没触发退出逻辑。
根本问题不是“有没有悬停能力”,而是“当前交互是否由精细指针发起”。@media (hover: hover) 只回答前半句,后半句必须靠 pointer: fine 补上。
- 别写
@media (hover: hover) and (pointer: coarse)—— 这条永远不匹配,两者在规范里互斥 - 别信
@media (any-hover: hover)—— 它只要设备“有任一可悬停输入方式”就命中,连带手写笔、蓝牙鼠标都算,误触率极高 - iOS Safari 有些版本硬编码
hover: hover为 true,哪怕你正用手指点,也骗不过它
正确写法:只对鼠标/触控笔生效的 @media (hover: hover) and (pointer: fine)
这条媒体查询才是安全守门员:它要求主输入机制既支持悬停,又属于精细类型(如鼠标、触控笔),自动排除所有手指触摸、触控板粗粒度场景。
示例:
@media (hover: hover) and (pointer: fine) {
.card {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
}
}
-
transition也得包进这个媒体查询里,否则移动 Safari 可能解析到 transition 属性,导致 Vue/React 组件重渲染时意外动画 - 别用
transition: all,明确列出要过渡的属性,避免副作用 - 这条规则不会影响
:focus或键盘导航,:focus样式仍需单独保留
移动端点击必须有 :active 替代反馈,且 iOS Safari 要求可点击性标记
禁用 :hover 不等于完成响应式 —— 用户点下去没视觉变化,会怀疑没点中。这时候 :active 就是移动端的瞬时反馈替代方案。
- iOS Safari 默认不触发
:active,除非元素或其祖先有cursor: pointer或绑定了touchstart事件(最简做法:) -
:active动画持续时间建议 ≤ 0.15s,避免点击后延迟感 - 不要用
!important强行覆盖:hover,而是让:active自身定义完整状态(背景、缩放、边框等) - 对非
<button></button>的<div>,务必加 <code>tabindex="0",否则:active和:focus都不会生效别用
@media (pointer: coarse)去“禁用 hover”,它职责不同@media (pointer: coarse)的本意是优化触屏体验,比如增大点击热区、调整 focus outline 尺寸、禁用拖拽手柄,而不是用来开关:hover的开关。- 错误写法:
@media (pointer: coarse) { .card:hover { display: none; } }—— Surface Pro 接着鼠标时也会失效,因为它的pointer特性随输入方式动态切换,但媒体查询只在页面加载时计算一次 - 正确做法:把
@media (hover: hover) and (pointer: fine)和@media (pointer: coarse)当作两个独立通道,各自承担明确职责 - 两者可共存,但不能混用;更不能写成
@media (hover: hover), (pointer: coarse)—— “或”逻辑反而扩大误触发范围
真正难处理的不是语法,而是设备输入模式的动态性:Surface 切到平板模式,
pointer变coarse,但hover可能还剩一点残余;三星手机连了蓝牙鼠标,hover突然又生效。这些边界情况没法靠单次媒体查询穷举,必须配合 JS 打has-touch类兜底,且所有依赖 hover 的功能都得有明确的 tap/click 替代路径。 - 错误写法:











