@media (prefers-reduced-motion) 可检测用户“减少动画”偏好,返回布尔信号;需用 animation-duration: 0.01ms、transition-duration: 0.01ms 等重置所有动效属性,并加 !important 保证覆盖。

如何用 @media (prefers-reduced-motion) 检测用户动画偏好
现代操作系统(macOS、Windows、iOS、Android)都提供“减少动画”开关,开启后会向网页传达信号。CSS 通过 @media (prefers-reduced-motion) 查询就能捕获这个设置,无需 JavaScript 干预。
注意:该媒体查询返回的是布尔值,只判断是否“启用减少动画”,不区分“关闭/自动/强制”等中间状态——浏览器只暴露一个二元信号。
- 在 macOS 中对应「系统设置 → 辅助功能 → 显示 → 减少运动」
- Windows 是「设置 → 辅助功能 → 视觉效果 → 简化动画」
- iOS/iPadOS 是「设置 → 辅助功能 → 动作 → 减少动态效果」
- 部分 Linux 桌面环境(如 GNOME)也支持,但需启用
gtk-enable-animations=0等配置
怎么写才真正禁用掉 CSS 动画和过渡
光写 @media (prefers-reduced-motion) 不够,关键是要覆盖掉原有动画逻辑。常见错误是只设 animation: none,却漏掉 transition 或 transform 的副作用。
推荐做法是统一重置所有可能触发视觉变化的属性:
立即学习“前端免费学习笔记(深入)”;
@media (prefers-reduced-motion) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}- 用
0.01ms而非0s:某些浏览器(如旧版 Safari)对0s的动画仍会触发渲染流水线,0.01ms更稳妥 - 必须加
!important:否则容易被组件库或内联样式覆盖 - 避免只针对特定类名:用通配符
*或:root+inherit更可靠
JavaScript 里怎么同步响应这个偏好
CSS 媒体查询本身不触发 JS 事件,但可以监听 window.matchMedia 的变化。
直接读取当前状态:
const motionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
if (motionQuery.matches) {
// 用户启用了减少动画
}- 不要用
'(prefers-reduced-motion: true)':语法错误,正确值只有reduce(规范定义) - 监听变化要主动调用
motionQuery.addEventListener('change', handler),Safari 14+ 才支持,旧版需用addListener - 服务端无法直接获取该值:它纯客户端,不能通过 User-Agent 或 HTTP Header 可靠判断
哪些动画最容易被忽略但必须处理
很多团队只关注 fade、slide 这类显性动效,却漏掉隐式触发的视觉反馈:
-
:focus-visible的 outline 动画(比如outline-offset渐变) - 第三方 UI 库的 tooltip、dropdown 展开(如 Bootstrap 的
fade类、Ant Design 的zoom过渡) - 滚动锚点平滑滚动:
scroll-behavior: smooth必须在媒体查询中设为auto - 背景渐变切换、
opacity微调、甚至box-shadow的扩散动画
最省事的方式是在根元素上加一个 data 属性,再用 CSS 层叠控制:
document.documentElement.dataset.motion = motionQuery.matches ? 'reduced' : 'normal';
然后写 [data-motion="reduced"] .tooltip { transition: none; } ——比全局重置更可控,也方便做灰度发布。
真正难的不是写几行 CSS,而是把动效从设计系统里“解耦”出来:每个可动画的组件都要预留 reduced-motion 分支,而不是等上线后被 a11y 审计打回来再补。


















