真正可用的方案是组合使用 CSS 显隐 + JS 控制状态 + 合理 DOM 结构:按钮组置于.card-body底部,用 position: absolute 定位,配合 opacity、pointer-events 和 transition;需支持 :focus-within、touchstart 及键盘导航,并设置 aria-label 与 z-index 避免遮挡。

Card悬停显示按钮组,别用title或纯CSS:hover硬撑
直接在<div class="card">上加title="编辑 删除"完全无效——Bootstrap 5 的 Tooltip 不会自动读取title属性;而只靠.card:hover .btn-group { opacity: 1; }容易导致按钮点击失效、移动端无反馈、键盘用户不可访问。真正可用的方案是组合使用 CSS 显隐 + JS 控制状态 + 合理 DOM 结构。
按钮组必须放在.card-body内部,且用opacity + pointer-events控制交互
把按钮组塞进.card-header或浮动到卡片右上角,极易被overflow: hidden裁剪,或与 Bootstrap 的.card-img-top重叠。稳妥做法是:把按钮组放在.card-body底部,用position: relative和margin-top: auto推到底部对齐。
-
.card-body需设position: relative,按钮组用position: absolute; bottom: 0.5rem; right: 0.5rem; - 按钮组默认
opacity: 0; pointer-events: none;,悬停时改为opacity: 1; pointer-events: all; - 务必加
transition: opacity 0.2s ease;,避免“啪”一下闪出 - 不要用
visibility: hidden——它会让按钮无法获得焦点,破坏键盘导航
移动端和键盘用户不能被忽略,得补:focus-within和touchstart逻辑
iOS Safari 和部分安卓浏览器对:hover支持极弱,纯靠悬停在触摸设备上等于功能缺失。同时,仅靠鼠标悬停也不满足 WCAG 2.1 对键盘可访问性的要求。
- 给
.card加tabindex="0",让它能被 Tab 键聚焦 - CSS 中补充
.card:focus-within .btn-group, .card:hover .btn-group双触发条件 - 用 JS 监听
touchstart事件,在首次触摸后给.card加has-touch类,然后改用.card.has-touch:active .btn-group激活(避免误触) - 按钮本身必须有
aria-label,例如<button aria-label="编辑此卡片">✏</button>
按钮组内链接或图标点击失效?检查z-index和transform干扰
如果卡片用了transform: translateY(-4px)抬升效果,按钮组可能被其他卡片或父容器遮挡——尤其当卡片在.row > .col中排列时,transform会创建新的 stacking context,导致z-index失效。
- 给
.card加z-index: 1;,按钮组加z-index: 2;(别依赖默认值) - 避免在
.card上同时用transform和box-shadow做悬停动画——它们会竞争渲染层,某些低端 Android 设备上按钮点击区域会偏移 - 测试时打开 Chrome DevTools → Rendering → “Paint flashing”,看按钮区域是否被意外裁剪或未绘制
pointer-events: none没配对关闭,或者忘了tabindex,结果鼠标能点、键盘不能操作,一上线就被无障碍审计打回来。动手前先用 Tab 键从头走到尾,再拿手机点两下,比写十行 CSS 都管用。


















