AI-consumed reference. Taste + technique for motion; the perf ceiling is owned by
rules/agent/frontend-excellence.md— cross-reference its budget, don't restate it.
Motion Design
Motion should clarify, not decorate. Small, fast, purposeful. House style: subtle — no bouncy/springy overshoot as decoration, no infinite loops competing for attention.
prefers-reduced-motion is MANDATORY
Every motion example MUST honor it — non-negotiable. Wrap transitions so reduced-motion collapses them to an instant (opacity-only or none):
@media (prefers-reduced-motion: reduce) { *, ::before, ::after { animation: none !important; transition: none !important; } }
In Framer Motion, gate variants on useReducedMotion() and drop transforms.
Two tools, matched to impact
- CSS-only for reveals/stagger (cheapest, no JS): stagger via
animation-delay: calc(var(--i) * 60ms)on a keyframed fade-up. Use for hero/list entrance where you don't need interaction. - Framer Motion for stateful/interactive motion: declarative
variants+initial/animate, shared layout, gesture. Prefer it only where CSS can't express the state.
Bundle discipline
Framer Motion is heavy — use LazyMotion + m. components (load domAnimation features on demand) instead of the full motion. import. This keeps it within the frontend-excellence perf budget.
Audit before shipping
- AnimatePresence gaps: exit animations silently do nothing unless the leaving element is wrapped in
<AnimatePresence>. Check every conditional/route unmount. - Reduced-motion path actually tested (not just declared).
See also: rules/agent/frontend-excellence.md, and the frontend-aesthetics skill.