@media (prefers-reduced-motion: reduce) 是唯一正确写法,必须小写、加英文双引号;它仅匹配 reduce 和 no-preference 两个值,true 等写法无效;需全局覆盖所有动画路径,包括 CSS transition/animation、内联样式、JS 动画及隐式插值。

必须用 @media (prefers-reduced-motion: reduce),写成 true、漏引号、大小写错误(如 Reduce)都会让规则完全失效。
为什么 @media (prefers-reduced-motion: true) 不工作
这个媒体查询只接受两个标准化值:reduce 和 no-preference,没有布尔类型。写成 true、1 或不加引号的 reduce 是语法错误,浏览器直接忽略整条规则——DevTools 里该媒体查询块会变灰,就是最明确的失效信号。
iOS Safari 尤其严格:必须是英文双引号包裹的小写 "reduce",即 @media (prefers-reduced-motion: "reduce") 才能稳定生效(Safari 16.4+ 强制校验)。
- macOS / Windows / iOS / Android 开启“减少动态效果”后,触发的始终是
reduce,不是 JS 变量 - 别依赖
no-preference写降级逻辑——它只是默认态,不表示“允许动画”,也不触发重绘 - 媒体查询不支持
or逻辑,不能写成(prefers-reduced-motion: reduce), (print)合并处理
怎么写才能覆盖所有动画路径,不漏掉隐式过渡
仅在 hover 或 active 状态里声明 transition 是危险的。移出时浏览器无过渡定义,就会硬切跳变,@media 根本没机会介入。
立即学习“前端免费学习笔记(深入)”;
必须把基础过渡写在默认选择器中,再用媒体查询覆盖:
button {
transition: transform 0.2s ease, opacity 0.2s ease;
}
@media (prefers-reduced-motion: reduce) {
button {
transition-duration: 0.01ms !important;
}
}
- 内联样式(
style="animation: spin 1s")无法被 CSS 媒体查询覆盖,得用 JS 动态控制或提前移除 - 第三方组件(如
.ant-collapse-content-active)若只在展开态定义transition,也要补上默认态声明 - 除了
animation和transition,transform、opacity、max-height、scroll-behavior都得在媒体查询里显式重置
JS 动画怎么同步响应,而不是“假装降级”
CSS 媒体查询管不到 requestAnimationFrame、GSAP、轮播自动播放或 Canvas 动画。必须手动监听并跳过:
const motionQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
if (!motionQuery.matches) {
startParallax();
}
motionQuery.addEventListener("change", () => {
if (motionQuery.matches) {
stopParallax();
} else {
startParallax();
}
});
- Safari 14+ 支持
change事件;旧版需轮询motionQuery.matches - 对骨架屏、loading 等场景,不要等动画结束,直接设终态:
el.style.opacity = "1"; el.style.transform = "none"; - 最容易被忽略的是隐式插值:即使没写
transition,JS 直接改transform或opacity,浏览器仍会平滑过渡——得在 CSS 媒体查询里强制transform: none !important; opacity: 1 !important;
真正难的不是写对那行 @media,而是确认所有动画路径都被截断:CSS 规则、内联样式、第三方库动效、JS requestAnimationFrame、甚至滚动行为。漏掉任意一环,用户开启“减少动画”后仍会看到晃动或跳变。


















