波纹效果需用:focus-within和data-ripple纯CSS实现,动态伪元素从点击中心缩放,配合自适应尺寸、统一阴影过渡、touch-action及focus-visible保障可访问性与多端兼容。

波纹效果的本质是伪元素动画,不是靠 hover 类简单叠加
很多人试过给按钮加 transition-all 和 shadow-lg hover:shadow-xl,发现点击时没有波纹——因为 Tailwind 默认不提供「点击中心扩散」的动画类。波纹需要动态生成一个圆形 ::after 伪元素,并从点击坐标开始缩放。Tailwind 的 animate- 类只支持预定义关键帧(如 animate-pulse),无法响应点击位置,所以必须手写少量 CSS 配合 Tailwind 的过渡与阴影工具类。
用 :focus-within + data-ripple 触发伪元素,避免依赖 JavaScript 状态管理
纯 CSS 实现波纹的关键在于:不监听 mousedown,而是利用按钮获得焦点(比如鼠标点击或空格键触发)时的 :focus-within 状态,配合自定义属性控制伪元素显隐。这样既保持可访问性,又减少 JS 介入。
- 给按钮加
relative overflow-hidden focus:outline-none——relative是为了让伪元素能绝对定位,overflow-hidden裁剪扩散出界的圆形 - 添加
data-ripple="true"属性(仅作 CSS 选择器用,无需 JS 设置) - 在 CSS 中写:
.btn[data-ripple="true"]:focus-within::after { content: ""; position: absolute; border-radius: 9999px; background-color: rgba(255,255,255,0.3); transform: scale(0); transition: transform 0.4s ease-out; } .btn[data-ripple="true"]:focus-within::after { width: 200px; height: 200px; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(1); } - 注意:上面的
width/height是固定值,实际中建议用 JS 动态设为按钮宽高较大者的 2 倍,否则小按钮波纹溢出、大按钮波纹太小
阴影类名要和波纹节奏对齐,否则视觉割裂
如果按钮本身用了 shadow-md hover:shadow-lg active:shadow-sm,但波纹动画持续 400ms,而 active:shadow-sm 只在按下瞬间生效(约 100ms),就会看到阴影先缩再扩,和波纹不同步。解决方式是统一用 CSS 过渡控制阴影变化,而非依赖 active: 工具类。
- 删掉所有
active:shadow-*类,改用transition-shadow+ 自定义 focus 状态阴影 - 例如:
shadow-md focus:shadow-lg focus:ring-2 focus:ring-blue-300 focus:ring-opacity-50—— 这样聚焦时阴影变大+带柔光环,和波纹动画节奏一致 - 如果要模拟「按下凹陷感」,可用
focus:-translate-y-0.5配合transition-transform,比改阴影更直观
移动端点击无反馈?补上 touch-action: manipulation 和 focus-visible
iOS Safari 和部分安卓浏览器默认禁用非表单元素的 :focus,导致 :focus-within 不触发。同时,键盘用户需要明确的焦点指示,不能只靠波纹。
立即学习“前端免费学习笔记(深入)”;
- 按钮必须是
<button>或带tabindex="0"的可交互元素,不能是<div>+onclick - 加
touch-action: manipulation(用class="touch-manipulation"并在 CSS 中定义)加速触摸响应 - 用
focus-visible替代单纯focus,避免鼠标点击时出现多余焦点环:.btn[data-ripple="true"]:focus-visible::after { ... },再配合focus:outline-none focus-visible:outline-2 focus-visible:outline-blue-500 - 别忘了设置
cursor: pointer,否则某些安卓 WebView 下点击无手型


















