必须同时满足四个条件:显式声明transition、提高选择器权重、避免父容器overflow:hidden裁剪阴影、兼顾移动端触发机制,缺一不可。
直接加 .card:hover { background-color: #f8f9fa; box-shadow: 0 0.5rem 1rem rgba(0,0,0,.15); } 是无效的——缺过渡、被覆盖、父容器裁剪、移动端不触发,四个环节漏一个就白写。
必须显式声明 background-color 和 box-shadow 的 transition
Bootstrap 5 的 .card 默认没写 transition,哪怕你写了 hover 状态,浏览器也不会“滑动”变色或投影,要么卡顿跳变,要么 Safari 直接忽略。
-
.card常态规则里必须加:transition: background-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out; - 别用
transition: all——它会把 border、outline 甚至 margin 变化也纳入过渡,干扰焦点感知 - 如果只想要背景色渐变,而投影是“开/关”式切换(非渐进加深),可只过渡
background-color,投影用box-shadow: 0 0 0 transparent初始值保兼容
选择器权重不够时,.card:hover 会被 Bootstrap 默认样式压住
Bootstrap 5 的 .card 自带 background-color: var(--bs-card-bg) 和 box-shadow: 0 0 0 rgba(0,0,0,0),这两条声明优先级高,纯 .card:hover 很可能被覆盖。
- 调试阶段可用
!important快速验证是否为权重问题:.card:hover { background-color: #e9ecef !important; box-shadow: 0 0.5rem 1rem rgba(0,0,0,.15) !important; } - 上线前务必改用更具体选择器,例如:
.card.card-hoverable:hover,并在 HTML 中加class="card-hoverable" - 若卡片在
.card-group内,它自带overflow: hidden,会直接砍掉投影——此时再高的选择器权重也没用
父容器 overflow: hidden 是阴影消失最常被忽略的原因
box-shadow 绘制在盒模型外部,只要祖先元素设了 overflow: hidden(比如 .row、.container-fluid、自定义 wrapper),阴影就会被硬裁切,和 CSS 权重无关。
- 临时排查:给疑似父容器加
outline: 1px solid red,看阴影是否刚好卡在 outline 边上 - 稳妥解法:删掉或改为
overflow: visible;若结构受限,给.card加position: relative; z-index: 1;,并确认无兄弟元素用更高z-index盖住它 - 特别注意
.card-group—— 它内部强制overflow: hidden,想保留阴影就得换布局,用.row + .col或 Flex 容器替代
移动端真机点按不触发 :hover,纯 CSS 方案不可靠
iOS Safari 和 Android Chrome 在真实设备上,单次点击不会触发 :hover,这是规范行为,不是 bug。如果你依赖“点一下变色+投影”来暗示可交互,纯 CSS 就会失效。
- 底线方案:给
.card加tabindex="0",再写.card:focus { background-color: #e9ecef; box-shadow: ...; },兼顾键盘与触摸 - 更可控做法:用 JS 切换类,例如
element.classList.toggle('card-hover-active'),CSS 中定义该类的样式 - 别信
@media (hover: hover)—— iOS Safari 对这个媒体查询支持极不稳定,真机常返回false,不能作为判断依据
真正难的不是写那两行 hover 样式,而是得同时盯住 transition 声明位置、选择器权重、父容器 overflow、移动端触发机制这四个点——少一个,效果就断在半路。











