Comprehensive Rules for creating performant, accessible, and maintainable web UI animations using CSS, Web Animations API, and modern JavaScript/TypeScript libraries such as Framer Motion and GSAP.
Stop wrestling with animation performance issues, accessibility oversights, and scattered animation code across your codebase. These comprehensive Cursor Rules turn your IDE into an animation development powerhouse, automatically enforcing best practices that eliminate common pitfalls and accelerate your workflow.
You've been there: animations that look perfect in development but stutter on production devices. Components that break when users have motion sensitivity preferences enabled. GSAP timelines that leak memory on route changes. Three.js scenes that tank frame rates on mobile.
The real problem isn't complexity—it's consistency and performance awareness across your entire animation codebase.
Most teams treat animations as an afterthought, leading to:
prefers-reduced-motionThese Cursor Rules establish a production-grade animation development environment that automatically guides you toward performant, accessible, and maintainable animation code. Every suggestion, every autocomplete, every error prevention is calibrated for real-world animation challenges.
What you get:
// Automatic TypeScript interfaces for animation configs
interface FadeConfig {
duration: number;
ease?: Ease;
delay?: number;
}
// Hardware-accelerated property enforcement
const slideIn = (el: HTMLElement) =>
el.animate({
transform: ['translateX(-100%)', 'translateX(0)'], // ✅ GPU-accelerated
opacity: [0, 1]
}, { duration: 300 });
// Automatic reduced-motion handling
@media (prefers-reduced-motion: reduce) {
.fade-enter-active {
transition-duration: 1ms !important;
}
}
transform, opacity)width, height, top, leftrequestAnimationFrame over setTimeout for smooth motionprefers-reduced-motion implementationsAbortController patterns for animation cleanupAnimatePresence usage, motionValue optimizationsuseFrame loops, performance budgeting, disposal patterns// Scattered across components, no types, potential memory leaks
useEffect(() => {
const tl = gsap.timeline();
tl.from('.hero', { opacity: 0, y: 40 });
// No cleanup - memory leak waiting to happen
}, []);
// Generated by Cursor Rules - typed, reusable, clean
const heroTimeline = (el: Element): gsap.core.Timeline =>
gsap.timeline().from(el, { opacity: 0, y: 40 });
useEffect(() => {
if (!heroRef.current) return;
const tl = heroTimeline(heroRef.current);
return () => tl.kill(); // Automatic cleanup
}, []);
Instead of mixing imperative animations with React state, you get clean, declarative patterns:
// Auto-generated Framer Motion patterns
const cardVariants = {
hidden: { opacity: 0, y: 20 },
enter: { opacity: 1, y: 0, transition: { duration: 0.3 } },
exit: { opacity: 0, y: -20, transition: { duration: 0.2 } }
} satisfies Variants;
export const AnimatedCard = ({ isVisible }: Props) => (
<motion.div
variants={cardVariants}
initial="hidden"
animate={isVisible ? "enter" : "hidden"}
exit="exit"
>
{children}
</motion.div>
);
Automatic patterns for animation performance tracking:
// Generated performance monitoring
const measureAnimationFrame = () => {
const start = performance.now();
// Animation logic here
const duration = performance.now() - start;
if (duration > 16) console.warn('Animation frame budget exceeded:', duration);
};
.cursor-rules filesrc/
├── animations/
│ ├── easings.ts # Centralized easing constants
│ ├── variants.ts # Framer Motion variants
│ └── utils.ts # Reusable animation utilities
The rules automatically generate your animation token system:
/* Auto-generated in your CSS */
:root {
--anim-fast: 150ms;
--anim-normal: 300ms;
--anim-slow: 600ms;
--ease-out-quad: cubic-bezier(0.25, 1, 0.5, 1);
}
For GSAP projects, the rules generate your setup file:
// src/animations/gsapSetup.ts - Auto-generated
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
export { gsap };
Start creating components and watch the rules guide you toward optimal patterns:
// Cursor automatically suggests this pattern
export const useSlideIn = (trigger: boolean) => {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!ref.current) return;
const animation = ref.current.animate(
{ transform: ['translateY(20px)', 'translateY(0)'], opacity: [0, 1] },
{ duration: 300, easing: 'ease-out' }
);
return () => animation.cancel();
}, [trigger]);
return ref;
};
Stop fighting animation performance issues and accessibility oversights in production. These Cursor Rules transform your animation development workflow from reactive debugging to proactive, performance-first development.
Your users deserve smooth, accessible animations. Your development team deserves tools that make excellence the default path.
You are an expert in HTML5, CSS3, TypeScript, Web Animations API, React, Framer Motion, GSAP, Three.js, and modern build tooling (Vite/Webpack).
Key Principles
- Favor performance, accessibility, and consistency over visual flair.
- Progressive enhancement: basic functionality must work with animations disabled.
- Respect `prefers-reduced-motion`; provide non-motion alternatives.
- Compose small, reusable animation utilities instead of monolithic files.
- Keep CSS in charge of simple state changes; use JS libraries only for advanced timelines or physics.
- Co-locate animation code with the component it animates; avoid global mutation.
TypeScript / JavaScript Rules
- Always type animation config objects:
```ts
interface FadeConfig { duration: number; ease?: Ease; delay?: number }
```
- Use `requestAnimationFrame` (rAF) or library abstractions; never `setInterval`/`setTimeout` loops for motion.
- Centralize easing constants in `src/animations/easings.ts`:
```ts
export const EASE_OUT_QUAD = [0.25, 1, 0.5, 1] as const;
```
- Camel-case variables/functions (`fadeIn`, `slideUpTl`).
- Side-effect-free helpers return animation objects rather than mutating DOM:
```ts
export const createPulse = (el: HTMLElement) =>
el.animate({ transform: ['scale(1)', 'scale(1.05)', 'scale(1)'] }, { duration: 600 });
```
- Use `AbortController` or timeline `kill()` for cancelation on component unmount.
- Prefer `for…of` over `Array.prototype.forEach` for large node lists to avoid GC churn.
CSS / SCSS Rules
- Kebab-case class names: `.btn--shake`, `.card-enter`.
- Declare motion tokens in `:root`:
```css
:root {
--anim-fast: 150ms;
--anim-normal: 300ms;
--anim-slow: 600ms;
}
```
- Use hardware-accelerated props (`transform`, `opacity`).
- Never animate `top`, `left`, `width`, `height`, or other layout-triggering properties.
- Pair state modifiers with data attributes, e.g. `[data-active="true"]` for transition triggers.
- Provide reduced-motion variant:
```css
@media (prefers-reduced-motion: reduce) {
.fade-enter-active { transition-duration: 1ms !important; }
}
```
Error Handling & Validation
- Check browser support:
```ts
if (!('animate' in HTMLElement.prototype)) fallbackFade(el);
```
- Wrap library calls in `try/catch`; log with contextual component name.
- Verify element existence before animating (`if (!el) return`).
- Handle timeline completion errors via `.catch(onAnimError)`.
- Provide graceful degradation: if GSAP not loaded, skip sequence but keep UX usable.
React + Framer Motion Rules
- Use `<motion.*>` elements; never mix React refs with imperative mutations inside the same component.
- Driving state via props > local `useState` when feasible to keep animation deterministic.
- Encapsulate variants:
```ts
const cardVariants = {
hidden: { opacity: 0, y: 20 },
enter: { opacity: 1, y: 0, transition: { duration: 0.3 } },
exit: { opacity: 0, y: -20, transition: { duration: 0.2 } }
} satisfies Variants;
```
- Add `layoutId` for shared-layout transitions.
- Use `AnimatePresence` for exit states and route transitions in Next.js pages.
- Use `motionValue` + `useTransform` for scroll-linked effects; throttle with `useViewportScroll`.
GSAP Rules
- Always register plugins once in a dedicated file `gsapSetup.ts`.
- Build timelines in a factory function and return them for control:
```ts
export const heroTimeline = (el: Element) => gsap.timeline().from(el, { opacity: 0, y: 40 });
```
- Kill timelines on route change or component unmount: `timeline.kill();`.
- Use `ScrollTrigger` with debounced refresh: `ScrollTrigger.refresh(true)`.
Three.js Rules
- Isolate animation loop inside React `useFrame` (with `@react-three/fiber`).
- Keep frame budget < 16ms; measure with `stats.js`.
- Dispose geometries and materials on unmount to prevent memory leaks.
Testing
- Cypress end-to-end tests: use `.should('have.css', 'opacity', '1')` after transition.
- Visual regression: integrate `Percy` or `Chromatic` snapshots at 100% & reduced-motion.
- Mock `prefers-reduced-motion` in tests to cover both paths.
Performance
- Code-split animation libraries (`import('framer-motion')`) when not critical to first paint.
- Tree-shake GSAP by importing scoped modules: `import { gsap } from 'gsap';` not whole bundle.
- Batch DOM writes: measure layout inside rAF, change styles in next frame.
Accessibility
- Provide non-motion affordances (e.g., outline changes) alongside motion.
- Ensure focus is not lost during page transitions.
- Limit animation duration < 700ms for repeat animations to reduce vestibular strain.
File / Directory Structure
- `src/animations/` – cross-app utilities
- `src/components/<feature>/` – each component co-locates `*.motion.tsx` file
- `static/lottie/` – exported Lottie JSON assets
Common Pitfalls & Guardrails
- Avoid autoplaying complex 3D scenes on low-end devices; gate via `navigator.hardwareConcurrency`.
- Do not chain more than 3 sequential animations without user interaction.
- Never block main thread with synchronous loops; offload heavy math to Web Worker if needed.