Skip to content
返回设计中心

prefers-reduced-motion 支持

为前庭敏感或偏好用户禁用非必要动画。

无障碍入门

为什么

视差、spinner、滑入动画可引发前庭敏感用户恶心、头晕或癫痫。prefers-reduced-motion 是 OS 级信号调低 — 忽略它是 WCAG 2.3.3 违规。

何时使用

始终。任何超 5ms 动画须尊重 prefers-reduced-motion。是 5 行 CSS 守卫或一 hook 检查 — 无借口跳过。

如何做

1. CSS 守卫 — @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } } 2. JS hook — const reduce = useReducedMotion(); 真时传 { duration: 0 } 给 framer-motion 3. 保留必要动效 — 状态变化 150ms 淡入可;杀视差、旋转、弹跳 4. 别全禁 — 即时状态变化(opacity 0→1)可;仅杀装饰动效 5. 测试 — 切 OS 设置(macOS 系统 → 无障碍 → 显示 → 减弱动效),验证 6. 库支持 — framer-motion useReducedMotion、GSAP matchMedia、CSS 原生处理 7. 播报 — 若动效承载信息(进度条),用文本或静态指示替代

示例

/* 全局守卫 — 杀装饰动效,留即时状态变化 */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; /* 无平滑滚动 */ } } // framer-motion — 分支 transition import { useReducedMotion } from "framer-motion"; function FadeIn({ children }: { children: React.ReactNode }) { const reduce = useReducedMotion(); return ( <motion.div initial={{ opacity: 0, y: reduce ? 0 : 8 }} animate={{ opacity: 1, y: 0 }} transition={reduce ? { duration: 0 } : { duration: 0.25, ease: "easeOut" }} > {children} </motion.div> ); }

常见问题

  • ⚠杀所有动效含 opacity — 状态变化不可见,用户错过更新
  • ⚠仅处理 CSS 非 JS 动画 — framer-motion 视差仍为减弱动效用户旋转
  • ⚠无测试 — 从未切 OS 设置即发布,破损动效上线

最佳实践

  • ✓留即时 opacity 变化 — 仅杀装饰动效(视差、旋转、弹跳、滑入)
  • ✓动画库用 JS hook — framer-motion 的 useReducedMotion 分支 transition
  • ✓切 OS 设置测试 — macOS 无障碍 → 显示 → 减弱动效,验证每屏

常见问题解答