design-audit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDesign Audit
设计审核
The final checkpoint. Loaded byat the end of the pipeline. Scans the codebase for motion gaps, a11y violations, perf issues, and inconsistencies./genjutsu:paint
最终检查点。在流程末尾通过加载。 扫描代码库,检查动效缺失、无障碍访问(a11y)违规、性能问题以及设计不一致情况。/genjutsu:paint
Motion Gap Analysis
动效缺失分析
Run these greps against the project to detect missing animations.
运行以下grep命令检测项目中缺失的动画。
Conditional renders without AnimatePresence
未使用AnimatePresence的条件渲染
bash
grep -rn '{.*&&\s*<\|{.*?\s*:\s*<\|{.*ternary.*<' --include='*.tsx' --include='*.jsx' src/ | grep -v 'AnimatePresence'Look for: or ternary renders without a wrapping . Every conditional mount/unmount needs exit animation support.
{show && <Component />}<AnimatePresence>bash
grep -rn '{.*&&\s*<\|{.*?\s*:\s*<\|{.*ternary.*<' --include='*.tsx' --include='*.jsx' src/ | grep -v 'AnimatePresence'查找类似或未包裹的三元渲染。所有条件挂载/卸载都需要支持退出动画。
{show && <Component />}<AnimatePresence>Hover states without transition
未添加过渡效果的悬停状态
bash
grep -rn ':hover' --include='*.css' --include='*.scss' --include='*.module.css' src/ | grep -vE 'transition|animation'Every rule must have a corresponding on the base selector. Instant state flips feel broken.
:hovertransitionbash
grep -rn ':hover' --include='*.css' --include='*.scss' --include='*.module.css' src/ | grep -vE 'transition|animation'每个规则必须在基础选择器上对应添加。状态瞬间切换会让用户体验不佳。
:hovertransitionDynamic lists without stagger
未添加 stagger 效果的动态列表
bash
grep -rn '\.map(' --include='*.tsx' --include='*.jsx' src/ | grep -vE 'stagger|delay.*index|variants|transition.*delay'Lists rendered via should stagger their entrance. Simultaneous pop-in looks cheap.
.map()bash
grep -rn '\.map(' --include='*.tsx' --include='*.jsx' src/ | grep -vE 'stagger|delay.*index|variants|transition.*delay'通过渲染的列表应该添加入场 stagger 效果。所有元素同时弹出会显得廉价。
.map()Style changes without transition
未添加过渡效果的样式变更
bash
grep -rn 'style={{' --include='*.tsx' --include='*.jsx' src/ | grep -vE 'transition|transform|opacity'Inline style changes (e.g., dynamic background, color) need a CSS transition or motion wrapper.
bash
grep -rn 'style={{' --include='*.tsx' --include='*.jsx' src/ | grep -vE 'transition|transform|opacity'内联样式变更(例如动态背景、颜色)需要添加CSS过渡效果或动效包装器。
Entries without corresponding exits
仅设置入场动画未设置对应退场动画
bash
grep -rn 'initial=' --include='*.tsx' --include='*.jsx' src/ | grep -v 'exit='Every Framer Motion + should have an prop when inside .
initialanimateexitAnimatePresencebash
grep -rn 'initial=' --include='*.tsx' --include='*.jsx' src/ | grep -v 'exit='在内部,每个Framer Motion的 + 都应该对应设置属性。
AnimatePresenceinitialanimateexitAccessibility Audit
无障碍访问审核
Reduced motion - MANDATORY
减少动效 - 强制要求
A project with animation MUST have at least one global handler matching its stack. Run all 3 greps:
bash
undefined包含动画的项目必须至少有一个匹配其技术栈的全局处理器。运行以下3条grep命令:
bash
undefinedWeb
Web
grep -rn 'prefers-reduced-motion' --include='.css' --include='.scss' --include='.ts' --include='.tsx' --include='.js' --include='.jsx' src/ 2>/dev/null
grep -rn 'prefers-reduced-motion' --include='.css' --include='.scss' --include='.ts' --include='.tsx' --include='.js' --include='.jsx' src/ 2>/dev/null
SwiftUI / UIKit
SwiftUI / UIKit
grep -rn 'accessibilityReduceMotion|isReduceMotionEnabled|reduceMotionStatusDidChangeNotification' --include='*.swift' . 2>/dev/null
grep -rn 'accessibilityReduceMotion|isReduceMotionEnabled|reduceMotionStatusDidChangeNotification' --include='*.swift' . 2>/dev/null
Compose
Compose
grep -rn 'LocalAccessibilityManager|isReduceTransitions|TRANSITION_ANIMATION_SCALE|ANIMATOR_DURATION_SCALE|areAnimatorsEnabled' --include='*.kt' . 2>/dev/null
**Zero results across all 3 in an animated project = critical violation.** At least one handler must exist somewhere. Cross-reference `motion-principles/SKILL.md` for canonical implementations per stack.grep -rn 'LocalAccessibilityManager|isReduceTransitions|TRANSITION_ANIMATION_SCALE|ANIMATOR_DURATION_SCALE|areAnimatorsEnabled' --include='*.kt' . 2>/dev/null
**如果包含动画的项目在所有3条命令中都无结果 = 严重违规。** 必须在某个位置存在至少一个处理器。参考`motion-principles/SKILL.md`获取各技术栈的标准实现方案。Contrast ratio 4.5:1
对比度4.5:1
- Use browser DevTools (Inspect > color swatch > contrast ratio)
- Run or Lighthouse accessibility audit
npx pa11y <url> - Check animated text at mid-transition -- fading text must remain readable at every opacity above 0.4
- 使用浏览器开发者工具(检查 > 颜色样本 > 对比度)
- 运行或Lighthouse无障碍访问审核
npx pa11y <url> - 检查过渡中的动画文本 -- 淡入淡出的文本在不透明度高于0.4的任何时刻都必须保持可读
Focus visible on all interactives
所有交互元素可见焦点
bash
grep -rn 'outline:\s*none\|outline:\s*0' --include='*.css' --include='*.scss' --include='*.module.css' src/Any MUST be paired with a custom style. Removing focus rings without replacement is a WCAG failure.
outline: none:focus-visiblebash
grep -rn 'outline:\s*none\|outline:\s*0' --include='*.css' --include='*.scss' --include='*.module.css' src/任何必须搭配自定义样式。移除焦点环而不替换属于WCAG违规。
outline: none:focus-visibleSemantic HTML -- no clickable divs
语义化HTML -- 禁止可点击div
bash
grep -rn 'onClick' --include='*.tsx' --include='*.jsx' src/ | grep -E '<div|<span' | grep -v 'role='Every or must either be a , an , or have + + .
<div onClick><span onClick><button><a>role="button"tabIndexonKeyDownbash
grep -rn 'onClick' --include='*.tsx' --include='*.jsx' src/ | grep -E '<div|<span' | grep -v 'role='每个或必须替换为、,或者添加 + + 。
<div onClick><span onClick><button><a>role="button"tabIndexonKeyDownARIA on decorative animations
装饰性动画的ARIA设置
bash
grep -rn '<motion\.\|<animated\.\|<Lottie\|<Canvas' --include='*.tsx' --include='*.jsx' src/ | grep -v 'aria-hidden'Purely decorative animations (background particles, ambient motion, Lottie illustrations) must have to avoid polluting screen readers.
aria-hidden="true"bash
grep -rn '<motion\.\|<animated\.\|<Lottie\|<Canvas' --include='*.tsx' --include='*.jsx' src/ | grep -v 'aria-hidden'纯装饰性动画(背景粒子、环境动效、Lottie插画)必须添加,避免干扰屏幕阅读器。
aria-hidden="true"Performance Audit
性能审核
Layout thrashing -- animating layout properties
布局抖动 -- 动画布局属性
bash
grep -rn 'transition.*\(width\|height\|top\|left\|right\|bottom\|margin\|padding\)' --include='*.css' --include='*.scss' --include='*.module.css' src/Animating layout properties triggers reflow every frame. Replace with and .
transform: translate/scaleopacitybash
grep -rn 'transition.*\(width\|height\|top\|left\|right\|bottom\|margin\|padding\)' --include='*.css' --include='*.scss' --include='*.module.css' src/为布局属性添加动画会触发每帧重排。替换为和。
transform: translate/scaleopacityExcessive paint triggers
过度触发绘制
bash
grep -rn 'will-change' --include='*.css' --include='*.scss' --include='*.module.css' src/will-changebash
grep -rn 'will-change' --include='*.css' --include='*.scss' --include='*.module.css' src/will-changeAnimation library bundle cost
动画库包体积成本
Check actual impact:
bash
npx source-map-explorer dist/**/*.js 2>/dev/null || npx vite-bundle-visualizer 2>/dev/nullReference sizes (gzipped): framer-motion ~30KB, GSAP ~25KB, popmotion ~5KB, CSS-only = 0KB. If the project only uses fade+slide, a 30KB lib is overkill.
On mobile native, the APK / IPA size matters. A third-party animation library adds typically 500KB-2MB:
| Library | Size impact (uncompressed) |
|---|---|
| Lottie (iOS) | ~600KB |
| Lottie (Android) | ~900KB |
| Rive (iOS) | ~1.5MB |
| Rive (Android) | ~2MB |
| Native Compose / SwiftUI animations | 0KB (built-in) |
If the project only uses fades, slides, and springs, native APIs (Compose + spring, SwiftUI ) are sufficient. Justify a Lottie / Rive dependency only for genuinely complex pre-designed animations (e.g., onboarding illustrations).
animate*AsStatewithAnimation检查实际影响:
bash
npx source-map-explorer dist/**/*.js 2>/dev/null || npx vite-bundle-visualizer 2>/dev/null参考体积(gzip压缩后):framer-motion ~30KB,GSAP ~25KB,popmotion ~5KB,纯CSS = 0KB。如果项目仅使用淡入淡出+滑动效果,30KB的库就过于冗余了。
在移动端原生开发中,APK/IPA体积很重要。第三方动画库通常会增加500KB-2MB体积:
| 库 | 体积影响(未压缩) |
|---|---|
| Lottie (iOS) | ~600KB |
| Lottie (Android) | ~900KB |
| Rive (iOS) | ~1.5MB |
| Rive (Android) | ~2MB |
| 原生Compose / SwiftUI动画 | 0KB(内置) |
如果项目仅使用淡入淡出、滑动和弹簧效果,原生API(Compose + spring,SwiftUI )就足够了。只有在处理真正复杂的预设计动画(例如引导页插画)时,才需要引入Lottie/Rive依赖。
animate*AsStatewithAnimationrequestAnimationFrame vs setTimeout
requestAnimationFrame vs setTimeout
bash
grep -rn 'setTimeout\|setInterval' --include='*.ts' --include='*.tsx' --include='*.js' --include='*.jsx' src/ | grep -iE 'anim\|motion\|scroll\|position\|style\|transform'Animation loops must use . / causes frame drops and doesn't pause in background tabs.
requestAnimationFramesetTimeoutsetIntervalbash
grep -rn 'setTimeout\|setInterval' --include='*.ts' --include='*.tsx' --include='*.js' --include='*.jsx' src/ | grep -iE 'anim\|motion\|scroll\|position\|style\|transform'动画循环必须使用。/会导致丢帧,且在后台标签页中不会暂停。
requestAnimationFramesetTimeoutsetIntervalConsistency Audit
一致性审核
Duration consistency
时长一致性
bash
grep -rnoE 'duration[:"'\''= ]+[0-9.]+' --include='*.tsx' --include='*.jsx' --include='*.ts' --include='*.css' --include='*.scss' src/ | sort -t: -k3 | uniq -c -f2 | sort -rnA well-designed project uses 3-5 distinct durations max (e.g., 0.15, 0.25, 0.35, 0.5). If you see 15 different values, extract them into a motion tokens file.
bash
grep -rnoE 'duration[:"'\''= ]+[0-9.]+' --include='*.tsx' --include='*.jsx' --include='*.ts' --include='*.css' --include='*.scss' src/ | sort -t: -k3 | uniq -c -f2 | sort -rn设计良好的项目最多使用3-5种不同的动画时长(例如0.15、0.25、0.35、0.5)。如果发现15种不同的值,应将它们提取到动效令牌文件中。
Easing inventory
缓动效果盘点
Run all 3 greps to inventory easing values across the codebase:
bash
undefined运行以下3条grep命令盘点代码库中的缓动值:
bash
undefinedWeb (CSS / JS / TSX)
Web (CSS / JS / TSX)
grep -rnoE 'ease[A-Za-z]|cubic-bezier([^)]+)|spring([^)])' --include='.tsx' --include='.jsx' --include='.ts' --include='.css' --include='*.scss' src/ 2>/dev/null
grep -rnoE 'ease[A-Za-z]|cubic-bezier([^)]+)|spring([^)])' --include='.tsx' --include='.jsx' --include='.ts' --include='.css' --include='*.scss' src/ 2>/dev/null
SwiftUI
SwiftUI
grep -rnoE '.spring([^)])|.snappy|.bouncy|.smooth|.linear(|.easeIn|.easeOut|.interpolatingSpring' --include='.swift' . 2>/dev/null
grep -rnoE '.spring([^)])|.snappy|.bouncy|.smooth|.linear(|.easeIn|.easeOut|.interpolatingSpring' --include='.swift' . 2>/dev/null
Compose
Compose
grep -rnoE 'spring([^)])|tween([^)])|FastOutSlowInEasing|LinearOutSlowInEasing|FastOutLinearInEasing|CubicBezierEasing' --include='*.kt' . 2>/dev/null
Same rule across all stacks: 3-5 named easings max in the design system. Random values scattered across files = visual inconsistency. If the codebase has 12 different `cubic-bezier(...)` values or 8 custom `spring(response:dampingFraction:)` configurations, that's a design-system violation, fix it by centralizing into named tokens.grep -rnoE 'spring([^)])|tween([^)])|FastOutSlowInEasing|LinearOutSlowInEasing|FastOutLinearInEasing|CubicBezierEasing' --include='*.kt' . 2>/dev/null
所有技术栈遵循相同规则:设计系统中最多使用3-5种命名缓动效果。随机值分散在各个文件中会导致视觉不一致。如果代码库中有12种不同的`cubic-bezier(...)`值或8种自定义`spring(response:dampingFraction:)`配置,这属于设计系统违规,应通过集中到命名令牌中来修复。Symmetric enter/exit
对称的入场/退场动画
Scan for motion components and verify that:
- Enter duration >= exit duration (never the reverse)
- Enter uses , exit uses
ease-outease-in - Enter has full choreography (translate + opacity + scale), exit is simpler (opacity only or opacity + slight scale)
bash
grep -A5 'exit=' --include='*.tsx' --include='*.jsx' -rn src/Compare and props side by side. Asymmetric timing (fast exit, slow enter) is correct. The reverse is wrong.
animateexit扫描动效组件并验证:
- 入场时长 >= 退场时长(绝不能反过来)
- 入场使用,退场使用
ease-outease-in - 入场包含完整的编排(位移 + 透明度 + 缩放),退场更简洁(仅透明度或透明度+轻微缩放)
bash
grep -A5 'exit=' --include='*.tsx' --include='*.jsx' -rn src/对比和属性。非对称时序(快速退场、缓慢入场)是正确的,反过来则错误。
animateexitStack-specific audit
技术栈专属审核
Pick the subsection matching the project stack.
选择与项目技术栈匹配的小节。
Compose (Android / Multiplatform)
Compose(Android / 跨平台)
- Run Layout Inspector (Android Studio): inspect recompositions, identify components recomposing on every state change.
- Run Macrobenchmark (): measure frame timing on a real device under representative scrolling / animation load. Target: <16.67ms per frame at 60fps, <8.33ms at 120fps.
androidx.benchmark.macro - Inspect recomposition counts via (Compose 1.6+) or Layout Inspector.
Modifier.recomposeHighlighter() - Generate Baseline Profiles () for production builds.
BaselineProfileGenerator - Verify is set on custom components (TalkBack support).
Modifier.semantics
- 运行Layout Inspector(Android Studio):检查重组情况,识别每次状态变更都会重组的组件。
- 运行Macrobenchmark():在真实设备上测量代表性滚动/动画负载下的帧时序。目标:60fps时每帧<16.67ms,120fps时每帧<8.33ms。
androidx.benchmark.macro - 通过(Compose 1.6+)或Layout Inspector检查重组次数。
Modifier.recomposeHighlighter() - 为生产构建生成Baseline Profiles()。
BaselineProfileGenerator - 验证自定义组件上是否设置了(支持TalkBack)。
Modifier.semantics
SwiftUI (iOS / macOS)
SwiftUI(iOS / macOS)
- Run Instruments Time Profiler: identify hot paths during animation (target: zero frames over 16.67ms).
- Run Instruments Hitches Instrument (iOS 14+): detects dropped frames and stalls.
- Run Instruments GPU Frame Capture for Metal shaders: verify shader compile time, GPU vs CPU bottleneck.
- Verify /
.accessibilityLabelon every interactive view..accessibilityHint - Test with Dynamic Type at 200% size (in Xcode).
Environment Overrides - Test with Reduce Motion ON.
- 运行Instruments Time Profiler:识别动画期间的热点路径(目标:无帧超过16.67ms)。
- 运行Instruments Hitches Instrument(iOS 14+):检测丢帧和卡顿。
- 为Metal着色器运行Instruments GPU Frame Capture:验证着色器编译时间、GPU与CPU瓶颈。
- 验证每个交互视图上是否设置了/
.accessibilityLabel。.accessibilityHint - 在200%动态字体大小下测试(Xcode中的)。
Environment Overrides - 在“减少动效”开启状态下测试。
Web
Web
- Existing checklist above (Lighthouse, Chrome DevTools Performance, etc.).
- 使用上述已有检查清单(Lighthouse、Chrome DevTools性能分析等)。
Output Format
输出格式
Structure findings by severity:
按严重程度整理发现的问题:
Critical (must fix before ship)
严重(发布前必须修复)
- Missing handling
prefers-reduced-motion - Clickable divs without keyboard support
- without
outline: nonereplacement:focus-visible - Animating layout properties (width/height/top/left)
- 缺失处理
prefers-reduced-motion - 可点击div无键盘支持
- 未搭配
outline: none替代样式:focus-visible - 为布局属性(width/height/top/left)添加动画
Important (fix in current sprint)
重要(当前迭代修复)
- Conditional renders without
AnimatePresence - Hover states without transition
- Missing on decorative animations
aria-hidden - used for animation loops
setTimeout - Inconsistent durations (>8 unique values)
- 条件渲染未使用
AnimatePresence - 悬停状态未添加过渡效果
- 装饰性动画未设置
aria-hidden - 使用实现动画循环
setTimeout - 动画时长不一致(超过8种唯一值)
Nice-to-have (backlog)
优化项(放入待办)
- Lists without stagger animation
- Inline styles without transition
- Excessive usage
will-change - Asymmetric enter/exit (wrong direction)
- Animation library oversized for actual usage
- 列表未添加stagger动画
- 内联样式未添加过渡效果
- 过度使用
will-change - 入场/退场动画不对称(方向错误)
- 动画库体积远超实际需求