必须用@media (prefers-reduced-motion: reduce)主动降级,因其仅支持reduce和no-preference两个规范值,写true会语法错误致样式失效;需分层控制动画时长、保留结构过渡并配合JS监听响应。

直接用 @media (prefers-reduced-motion: reduce) 就行,别写 JS 判断、别漏引号、别当成布尔值用——这是唯一在样式计算阶段就生效、所有现代浏览器稳定支持的方案。
为什么不能用 JS 检测来关动画
CSS 动画降级必须发生在样式计算和布局之前。JS 是运行时行为,会导致:
-
FOUC(页面闪一下动画才停),尤其在 Next.js / Nuxt 等 SSR 场景下明显 - 用户中途开关系统设置时,
window.matchMedia的change事件在 Safari 14 以下不触发,监听可能漏掉 - 旧版安卓 WebView 可能根本没实现
matchMedia,直接报错或返回false - 第三方组件(如 Ant Design)里内联的
animation属性,JS 根本控制不了
怎么写才不破坏功能又真有效
关键不是“一刀切禁掉所有动画”,而是让结构逻辑照常、视觉动效降级:
- 必须同时覆盖
animation和transition,只写一个会漏掉悬停/展开等交互动效 - 别写
(prefers-reduced-motion: true)—— 语法错误,正确值只有reduce或no-preference - 避免
* { animation: none !important }这种粗暴写法,它会干掉模态框焦点捕获、菜单max-height过渡等关键功能 - 推荐写法:
@media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01s !important; } [aria-expanded="true"], .is-open { opacity: 1; transition: opacity 0.1s ease; } }
哪些地方最容易被忽略
即使加了媒体查询,仍可能失效,因为:
立即学习“前端免费学习笔记(深入)”;
- JS 直接改
element.style.transform或opacity,浏览器仍会插值平滑过渡——需配合强制瞬时切换(如加transform: none !important) -
will-change没重置,会继续触发图层提升和额外渲染开销,建议一并设为will-change: auto !important - 自动轮播、Canvas 动画、GSAP 动画等 JS 驱动行为,CSS 媒体查询完全无效,必须用
window.matchMedia主动判断并在启动前跳过 - iOS Safari 对该特性响应更严格,某些自定义滚动条动画也会被静音,仅靠桌面 Chrome DevTools 模拟不够,得真机测试
最复杂的点不在写法本身,而在于区分“纯视觉动画”和“功能依赖过渡”——比如菜单展开靠 max-height 过渡保障可滚动性,这类不能一刀禁,得保留极简时长;而背景色脉冲、视差位移这类,才是该彻底关掉的。


















