必须使用@media (prefers-reduced-motion: reduce),iOS Safari要求双引号包裹小写"reduce";transition/animation须在默认样式声明,再用媒体查询覆盖;内联样式和JS动画需手动控制;监听window.matchMedia确保初始与动态状态准确。

必须用 @media (prefers-reduced-motion: reduce),其他写法全无效
浏览器只认小写 reduce,且不接受布尔值或数字。写成 @media (prefers-reduced-motion: true)、@media (prefers-reduced-motion: "True") 或漏掉引号的 reduce,整条规则会被直接忽略——DevTools 里该媒体查询块会变灰,就是最明确的失效信号。iOS Safari 尤其严格:必须是英文双引号包裹的小写 "reduce",即 @media (prefers-reduced-motion: "reduce") 才能稳定生效(Safari 16.4+ 强制校验)。
transition 和 animation 必须在默认样式中声明,否则@media 无法覆盖
仅在 :hover 或 .is-open 状态里加 transition 是危险的。移出时浏览器无过渡定义,就会硬切跳变,@media 根本没机会介入。
- 正确做法:把基础过渡写在默认选择器中,再用媒体查询覆盖
button { transition: transform 0.2s ease, opacity 0.2s ease; }@media (prefers-reduced-motion: reduce) { button { transition-duration: 0.01ms !important; } }- 第三方组件(如
.ant-collapse-content-active)若只在展开态定义transition,也得补上默认态声明
内联样式和 JS 动画完全不受 CSS 媒体查询控制
style="animation: spin 1s" 这类内联动画无法被 @media 覆盖,必须用 JS 动态控制或提前移除。JS 创建的动画(requestAnimationFrame、GSAP、轮播自动播放、Canvas 动画)同样需要手动监听并跳过。
- 用
window.matchMedia('(prefers-reduced-motion: reduce)')监听变化,比反复查getComputedStyle更准更高效 - 监听后立即调用一次回调函数,确保初始值正确(否则页面刚加载时状态为空)
- 旧版 Safari 需降级使用
media.addListener,而非addEventListener - React 等框架中,
framer-motion等第三方动画库需单独配置,不能依赖 CSS 媒体查询
静态状态必须独立于动画存在,否则“不动”就等于“不可见”
把 animation-duration 设为 0 后页面不动了,但选中标记却没出现——这说明业务状态被绑在动画结束回调里,或元素初始透明,最终样式只靠 animation-fill-mode: forwards 保留。减弱动态反馈不是取消反馈,而是保留可访问的状态表达。
立即学习“前端免费学习笔记(深入)”;
- 卡片的真实选中值、可见标记与可访问状态应在输入发生时更新,动画只是附加表达
- 装饰性效果应放在专用子层,不承担布局定位,也不携带唯一状态信息
- 关键信息(如加载提示、选中标识)在无动画下仍需清晰传达,不能依赖动画填充最终样式
-
transform、opacity、max-height、scroll-behavior都得在媒体查询里显式重置,不能指望一条transition: none搞定全部
@media,而是把所有隐式依赖动画的状态逻辑,拆解成“状态驱动 + 动效可选”的两层结构。这点在复杂组件或遗留代码里最容易被忽略。


















