@polymorphism-tech/morph-spec 7.0.0 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +367 -367
  2. package/framework/agents.json +132 -127
  3. package/framework/rubrics/backend.json +41 -36
  4. package/framework/rules/ai-agents-standards.md +45 -15
  5. package/framework/rules/neon-standards.md +25 -26
  6. package/framework/standards/STANDARDS.json +1729 -1133
  7. package/framework/standards/ai-agents/agent-session.md +160 -0
  8. package/framework/standards/ai-agents/batching.md +193 -0
  9. package/framework/standards/ai-agents/context-providers.md +188 -0
  10. package/framework/standards/ai-agents/mcp-server.md +250 -0
  11. package/framework/standards/ai-agents/mcp-tools.md +197 -0
  12. package/framework/standards/ai-agents/middleware-patterns.md +207 -0
  13. package/framework/standards/ai-agents/modalities-image-gen.md +173 -0
  14. package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -0
  15. package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -0
  16. package/framework/standards/ai-agents/modalities-vision.md +201 -0
  17. package/framework/standards/ai-agents/multi-agent-patterns.md +155 -0
  18. package/framework/standards/ai-agents/production.md +114 -258
  19. package/framework/standards/ai-agents/prompts-in-db.md +214 -0
  20. package/framework/standards/ai-agents/providers/model-registry.md +158 -0
  21. package/framework/standards/ai-agents/rag-custom-pgvector.md +243 -0
  22. package/framework/standards/ai-agents/service-tier-flex.md +135 -0
  23. package/framework/standards/ai-agents/setup.md +142 -316
  24. package/framework/standards/ai-agents/structured-output.md +249 -0
  25. package/framework/standards/ai-agents/sweet-spot.md +109 -0
  26. package/framework/standards/ai-agents/workflows.md +114 -284
  27. package/framework/standards/architecture/vertical-slice/vertical-slice.md +433 -429
  28. package/framework/standards/backend/api/validation.md +92 -88
  29. package/framework/standards/backend/database/postgresql/database.md +336 -332
  30. package/framework/standards/backend/database/vector-search-rag.md +599 -541
  31. package/framework/standards/backend/dotnet/fluent-validation-vsa.md +4 -0
  32. package/framework/standards/backend/dotnet/result-pattern.md +4 -0
  33. package/framework/standards/backend/dotnet/scalar-ui.md +176 -172
  34. package/framework/standards/backend/dotnet/scrutor.md +4 -0
  35. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +278 -274
  36. package/framework/standards/backend/integrations/asaas/asaas-api.md +260 -216
  37. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +354 -350
  38. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +30 -19
  39. package/framework/standards/backend/integrations/resend/resend-email.md +400 -385
  40. package/framework/standards/data/nosql/cache/redis.md +106 -97
  41. package/framework/standards/data/nosql/cosmos-db.md +127 -118
  42. package/framework/standards/data/vector-search/rag-chunking.md +113 -104
  43. package/framework/standards/frontend/design-system/ai-image-generation.md +4 -0
  44. package/framework/standards/frontend/design-system/animations.md +164 -160
  45. package/framework/standards/frontend/design-system/naming.md +68 -64
  46. package/framework/standards/frontend/gsap/gsap-core.md +14 -8
  47. package/framework/standards/frontend/gsap/gsap-plugins.md +26 -21
  48. package/framework/standards/frontend/gsap/gsap-scrolltrigger.md +9 -7
  49. package/framework/standards/frontend/nextjs/app-router.md +127 -123
  50. package/framework/standards/frontend/nextjs/components.md +136 -132
  51. package/framework/standards/frontend/nextjs/data-fetching.md +130 -126
  52. package/framework/standards/frontend/nextjs/forms.md +134 -128
  53. package/framework/standards/frontend/nextjs/motion-patterns.md +328 -322
  54. package/framework/standards/frontend/nextjs/naming-conventions.md +71 -67
  55. package/framework/standards/frontend/nextjs/nextjs-patterns.md +229 -225
  56. package/framework/standards/frontend/nextjs/project-structure.md +106 -102
  57. package/framework/standards/frontend/nextjs/state-management.md +76 -72
  58. package/framework/standards/frontend/nextjs/testing.md +115 -111
  59. package/framework/standards/infrastructure/docker/easypanel-deploy.md +196 -192
  60. package/framework/standards/infrastructure/neon/neon-pgrag.md +6 -2
  61. package/framework/standards/infrastructure/neon/neon-pgvector.md +4 -0
  62. package/framework/standards/infrastructure/neon/neon-setup.md +5 -1
  63. package/framework/standards/integration/mcp/mcp-tools.md +471 -462
  64. package/framework/templates/REGISTRY.json +1745 -1239
  65. package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -0
  66. package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -0
  67. package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -0
  68. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -0
  69. package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -0
  70. package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -0
  71. package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -0
  72. package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -0
  73. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -0
  74. package/package.json +89 -83
  75. package/src/core/templates/template-registry.js +289 -256
  76. package/src/lib/generators/context-generator.js +516 -518
  77. package/src/lib/generators/scaffold-generator.js +23 -26
  78. package/framework/standards/infrastructure/neon/neon-semantic-kernel.md +0 -310
  79. package/framework/templates/integrations/semantic-kernel-neon.cs +0 -125
@@ -1,322 +1,328 @@
1
- ---
2
- scope: frontend-nextjs-ui
3
- layer: 3
4
- keywords: framer-motion, animation, motion, transition, variants, gesture, scroll
5
- load_when: nextjs-expert active
6
- ---
7
-
8
- # Motion Patterns — Framer Motion
9
-
10
- Reference guide for implementing animations and transitions in Next.js projects using Framer Motion. Covers the canonical variants library, design token bridge, scroll and gesture patterns, loading states, and mandatory WCAG accessibility compliance.
11
-
12
- ---
13
-
14
- ## Section 1: Core Variants Library
15
-
16
- Place this file at `motion/variants.ts` in your Next.js project root.
17
-
18
- ```typescript
19
- import { Variants } from 'framer-motion';
20
- import { durations, easings } from '@/lib/design-tokens'; // maps to design-system.md motion tokens
21
-
22
- // Fade
23
- export const fadeIn: Variants = {
24
- hidden: { opacity: 0 },
25
- visible: { opacity: 1, transition: { duration: durations.base, ease: easings.standard } },
26
- };
27
-
28
- // Slide variations
29
- export const slideInUp: Variants = {
30
- hidden: { opacity: 0, y: 24 },
31
- visible: { opacity: 1, y: 0, transition: { duration: durations.base, ease: easings.decelerate } },
32
- };
33
- export const slideInDown: Variants = {
34
- hidden: { opacity: 0, y: -24 },
35
- visible: { opacity: 1, y: 0, transition: { duration: durations.base, ease: easings.decelerate } },
36
- };
37
- export const slideInLeft: Variants = {
38
- hidden: { opacity: 0, x: -24 },
39
- visible: { opacity: 1, x: 0, transition: { duration: durations.base, ease: easings.decelerate } },
40
- };
41
- export const slideInRight: Variants = {
42
- hidden: { opacity: 0, x: 24 },
43
- visible: { opacity: 1, x: 0, transition: { duration: durations.base, ease: easings.decelerate } },
44
- };
45
-
46
- // Scale
47
- export const scaleIn: Variants = {
48
- hidden: { opacity: 0, scale: 0.92 },
49
- visible: { opacity: 1, scale: 1, transition: { duration: durations.fast, ease: easings.bounce } },
50
- };
51
-
52
- // Stagger container + child
53
- export const staggerContainer: Variants = {
54
- hidden: {},
55
- visible: {
56
- transition: {
57
- staggerChildren: 0.08,
58
- delayChildren: 0.1,
59
- },
60
- },
61
- };
62
- export const staggerItem: Variants = {
63
- hidden: { opacity: 0, y: 16 },
64
- visible: { opacity: 1, y: 0, transition: { duration: durations.base, ease: easings.decelerate } },
65
- };
66
-
67
- // Page transition (for AnimatePresence in layout.tsx)
68
- export const pageTransition: Variants = {
69
- hidden: { opacity: 0, y: 8 },
70
- visible: { opacity: 1, y: 0, transition: { duration: durations.base, ease: easings.decelerate } },
71
- exit: { opacity: 0, y: -8, transition: { duration: durations.fast, ease: easings.accelerate } },
72
- };
73
- ```
74
-
75
- ---
76
-
77
- ## Section 2: Design Token Bridge
78
-
79
- This file bridges the CSS variables defined in `.morph/context/design-system.md` to JavaScript values consumed by Framer Motion. Place at `lib/design-tokens.ts`.
80
-
81
- ```typescript
82
- // lib/design-tokens.ts
83
- // Maps CSS variables from .morph/context/design-system.md to JS values for Framer Motion
84
- export const durations = {
85
- instant: 0.05,
86
- fast: 0.15,
87
- base: 0.3,
88
- slow: 0.5,
89
- slower: 0.8,
90
- } as const;
91
-
92
- export const easings = {
93
- standard: [0.4, 0, 0.2, 1] as [number, number, number, number],
94
- decelerate: [0, 0, 0.2, 1] as [number, number, number, number],
95
- accelerate: [0.4, 0, 1, 1] as [number, number, number, number],
96
- bounce: [0.34, 1.56, 0.64, 1] as [number, number, number, number],
97
- spring: [0.68, -0.55, 0.265, 1.55] as [number, number, number, number],
98
- };
99
- ```
100
-
101
- > **Rule**: Never hardcode duration numbers or easing arrays in component files. Always import from `@/lib/design-tokens`.
102
-
103
- ---
104
-
105
- ## Section 3: Scroll Animations
106
-
107
- Two approaches for triggering animations when elements enter the viewport:
108
-
109
- ```tsx
110
- 'use client';
111
- import { motion, useInView } from 'framer-motion';
112
- import { useRef } from 'react';
113
- import { fadeIn, slideInUp } from '@/motion/variants';
114
-
115
- // Option A: whileInView (simpler, one-shot)
116
- <motion.div
117
- variants={slideInUp}
118
- initial="hidden"
119
- whileInView="visible"
120
- viewport={{ once: true, margin: '-50px' }}
121
- >
122
-
123
- // Option B: useInView hook (more control)
124
- function AnimatedSection({ children }: { children: React.ReactNode }) {
125
- const ref = useRef(null);
126
- const isInView = useInView(ref, { once: true, margin: '-100px' });
127
- return (
128
- <motion.div ref={ref} variants={fadeIn} initial="hidden" animate={isInView ? 'visible' : 'hidden'}>
129
- {children}
130
- </motion.div>
131
- );
132
- }
133
- ```
134
-
135
- - Use `once: true` for content sections — animations should not replay on scroll up.
136
- - Adjust `margin` to fire slightly before the element fully enters the viewport.
137
- - Prefer `whileInView` for simple cases; use `useInView` when you need imperative control or conditional logic.
138
-
139
- ---
140
-
141
- ## Section 4: Gesture Interactions
142
-
143
- ```tsx
144
- import { durations, easings } from '@/lib/design-tokens';
145
-
146
- // Hover + tap on cards
147
- <motion.div
148
- whileHover={{ y: -4, boxShadow: 'var(--shadow-lift)' }}
149
- whileTap={{ scale: 0.98 }}
150
- transition={{ duration: durations.fast, ease: easings.standard }}
151
- >
152
-
153
- // Drag interaction
154
- <motion.div
155
- drag
156
- dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }}
157
- dragElastic={0.1}
158
- whileDrag={{ scale: 1.05, cursor: 'grabbing' }}
159
- >
160
- ```
161
-
162
- - `whileHover` and `whileTap` are inline — they do not need variant definitions for simple state changes.
163
- - Use `var(--shadow-lift)` from the design system rather than hardcoded box-shadow values.
164
- - `dragElastic={0.1}` gives a subtle rubber-band feel without being distracting.
165
-
166
- ---
167
-
168
- ## Section 5: AnimatePresence (Page Transitions)
169
-
170
- Integrate `AnimatePresence` at the layout level to animate between routes:
171
-
172
- ```tsx
173
- // app/layout.tsx
174
- 'use client';
175
- import { AnimatePresence, motion } from 'framer-motion';
176
- import { usePathname } from 'next/navigation';
177
- import { pageTransition } from '@/motion/variants';
178
-
179
- export default function Layout({ children }: { children: React.ReactNode }) {
180
- const pathname = usePathname();
181
-
182
- return (
183
- <html>
184
- <body>
185
- <AnimatePresence mode="wait">
186
- <motion.main
187
- key={pathname}
188
- variants={pageTransition}
189
- initial="hidden"
190
- animate="visible"
191
- exit="exit"
192
- >
193
- {children}
194
- </motion.main>
195
- </AnimatePresence>
196
- </body>
197
- </html>
198
- );
199
- }
200
- ```
201
-
202
- - `mode="wait"` ensures the exit animation completes before the entering page renders.
203
- - The `key` prop must change on navigation — use `usePathname()` from `next/navigation`.
204
- - Keep page transitions subtle (`y: 8`, fast duration) — aggressive transitions hurt perceived performance.
205
-
206
- ---
207
-
208
- ## Section 6: Loading Patterns
209
-
210
- Prefer CSS `animate-pulse` for skeleton loaders over Framer Motion — CSS animations run off the main thread and have lower performance overhead.
211
-
212
- ```tsx
213
- // components/skeleton.tsx
214
- import { cn } from '@/lib/utils';
215
-
216
- function Skeleton({ className }: { className?: string }) {
217
- return (
218
- <div
219
- className={cn(
220
- 'animate-pulse rounded-[var(--radius-md)] bg-[var(--color-neutral-200)]',
221
- className
222
- )}
223
- />
224
- );
225
- }
226
-
227
- // Skeleton card example
228
- function SkeletonCard() {
229
- return (
230
- <div className="p-[var(--space-6)] space-y-[var(--space-4)]">
231
- <Skeleton className="h-4 w-3/4" />
232
- <Skeleton className="h-4 w-1/2" />
233
- <Skeleton className="h-20 w-full" />
234
- </div>
235
- );
236
- }
237
- ```
238
-
239
- - Use design token CSS variables (`--radius-md`, `--color-neutral-200`, `--space-*`) — never hardcode dimensions or colors.
240
- - Skeleton shapes should mirror the actual content layout to minimize layout shift on load.
241
- - For spinner/progress indicators that need JS control, use a simple Framer Motion `animate={{ rotate: 360 }}` with `repeat: Infinity`.
242
-
243
- ---
244
-
245
- ## Section 7: WCAG Accessibility (MANDATORY)
246
-
247
- > **This section is non-negotiable.** All animated components must respect `prefers-reduced-motion`.
248
-
249
- ### Hook pattern — wrap all animated components
250
-
251
- ```tsx
252
- 'use client';
253
- import { motion, useReducedMotion } from 'framer-motion';
254
- import { slideInUp } from '@/motion/variants';
255
-
256
- function AnimatedCard({ children }: { children: React.ReactNode }) {
257
- const shouldReduceMotion = useReducedMotion();
258
-
259
- const variants = shouldReduceMotion
260
- ? { hidden: { opacity: 0 }, visible: { opacity: 1 } } // instant, no movement
261
- : slideInUp;
262
-
263
- return (
264
- <motion.div variants={variants} initial="hidden" animate="visible">
265
- {children}
266
- </motion.div>
267
- );
268
- }
269
- ```
270
-
271
- When `shouldReduceMotion` is `true`:
272
- - Remove all `x`, `y`, `scale` transforms.
273
- - Keep opacity transitions (they do not cause vestibular issues).
274
- - Do not set `transition: { duration: 0 }` — a very short fade is acceptable and less jarring than an instant pop-in.
275
-
276
- ### CSS fallback (add to global stylesheet)
277
-
278
- ```css
279
- @media (prefers-reduced-motion: reduce) {
280
- *, *::before, *::after {
281
- animation-duration: 0.01ms !important;
282
- transition-duration: 0.01ms !important;
283
- }
284
- }
285
- ```
286
-
287
- This CSS fallback catches animations outside Framer Motion (Tailwind `animate-*`, third-party components, CSS keyframes).
288
-
289
- ---
290
-
291
- ## Section 8: Anti-patterns
292
-
293
- | Anti-pattern | Correct approach |
294
- |---|---|
295
- | `duration: 0.8` for UI interactions | `duration: durations.fast` (0.15) for micro-interactions |
296
- | Hardcoded easing `[0.4, 0, 0.2, 1]` | `ease: easings.standard` (from design-tokens) |
297
- | No `useReducedMotion()` check | Always check or use CSS media query |
298
- | Animating everything on the page | Reserve motion for 3-5 meaningful moments per page |
299
- | Forgetting `animation-fill-mode: backwards` in stagger | Will cause a flash before the animation starts |
300
- | `AnimatePresence` without `key` changes | Exit animations will never fire |
301
- | Using `framer-motion` for skeleton loaders | Use CSS `animate-pulse` — lower performance cost |
302
- | Need ScrollTrigger, SVG morph, or complex timelines | Use GSAP — see `frontend/gsap/gsap-core.md` |
303
-
304
- ---
305
-
306
- ## Section 9: Installation
307
-
308
- ```bash
309
- npm install framer-motion
310
- ```
311
-
312
- > If using **shadcn/ui**, framer-motion is already included as a dependency — no additional install needed.
313
-
314
- Verify the installed version supports the features used here (requires framer-motion >= 10):
315
-
316
- ```bash
317
- npm list framer-motion
318
- ```
319
-
320
- ---
321
-
322
- *MORPH-SPEC by Polymorphism Tech*
1
+ # Motion Patterns — Motion for React
2
+
3
+ > **Scope:** frontend-nextjs-ui
4
+ > **Layer:** 3
5
+ > **Keywords:** motion, framer-motion, animation, transition, variants, gesture, scroll
6
+ > **Load When:** nextjs-expert active
7
+
8
+ **Verified against:** Motion for React 12 (`motion`, imports from `motion/react`). Last-verified: 2026-05-20.
9
+
10
+ ---
11
+
12
+ Reference guide for implementing animations and transitions in Next.js projects using **Motion for React** — the library formerly published as `framer-motion`. The package is now `motion` and components import from `motion/react`; the `framer-motion` name only survives inside the Framer design tool. Covers the canonical variants library, design token bridge, scroll and gesture patterns, loading states, and mandatory WCAG accessibility compliance.
13
+
14
+ ---
15
+
16
+ ## Section 1: Core Variants Library
17
+
18
+ Place this file at `motion/variants.ts` in your Next.js project root.
19
+
20
+ ```typescript
21
+ import { Variants } from 'motion/react';
22
+ import { durations, easings } from '@/lib/design-tokens'; // maps to design-system.md motion tokens
23
+
24
+ // Fade
25
+ export const fadeIn: Variants = {
26
+ hidden: { opacity: 0 },
27
+ visible: { opacity: 1, transition: { duration: durations.base, ease: easings.standard } },
28
+ };
29
+
30
+ // Slide variations
31
+ export const slideInUp: Variants = {
32
+ hidden: { opacity: 0, y: 24 },
33
+ visible: { opacity: 1, y: 0, transition: { duration: durations.base, ease: easings.decelerate } },
34
+ };
35
+ export const slideInDown: Variants = {
36
+ hidden: { opacity: 0, y: -24 },
37
+ visible: { opacity: 1, y: 0, transition: { duration: durations.base, ease: easings.decelerate } },
38
+ };
39
+ export const slideInLeft: Variants = {
40
+ hidden: { opacity: 0, x: -24 },
41
+ visible: { opacity: 1, x: 0, transition: { duration: durations.base, ease: easings.decelerate } },
42
+ };
43
+ export const slideInRight: Variants = {
44
+ hidden: { opacity: 0, x: 24 },
45
+ visible: { opacity: 1, x: 0, transition: { duration: durations.base, ease: easings.decelerate } },
46
+ };
47
+
48
+ // Scale
49
+ export const scaleIn: Variants = {
50
+ hidden: { opacity: 0, scale: 0.92 },
51
+ visible: { opacity: 1, scale: 1, transition: { duration: durations.fast, ease: easings.bounce } },
52
+ };
53
+
54
+ // Stagger container + child
55
+ export const staggerContainer: Variants = {
56
+ hidden: {},
57
+ visible: {
58
+ transition: {
59
+ staggerChildren: 0.08,
60
+ delayChildren: 0.1,
61
+ },
62
+ },
63
+ };
64
+ export const staggerItem: Variants = {
65
+ hidden: { opacity: 0, y: 16 },
66
+ visible: { opacity: 1, y: 0, transition: { duration: durations.base, ease: easings.decelerate } },
67
+ };
68
+
69
+ // Page transition (for AnimatePresence in layout.tsx)
70
+ export const pageTransition: Variants = {
71
+ hidden: { opacity: 0, y: 8 },
72
+ visible: { opacity: 1, y: 0, transition: { duration: durations.base, ease: easings.decelerate } },
73
+ exit: { opacity: 0, y: -8, transition: { duration: durations.fast, ease: easings.accelerate } },
74
+ };
75
+ ```
76
+
77
+ ---
78
+
79
+ ## Section 2: Design Token Bridge
80
+
81
+ This file bridges the CSS variables defined in `.morph/context/design-system.md` to JavaScript values consumed by Framer Motion. Place at `lib/design-tokens.ts`.
82
+
83
+ ```typescript
84
+ // lib/design-tokens.ts
85
+ // Maps CSS variables from .morph/context/design-system.md to JS values for Framer Motion
86
+ export const durations = {
87
+ instant: 0.05,
88
+ fast: 0.15,
89
+ base: 0.3,
90
+ slow: 0.5,
91
+ slower: 0.8,
92
+ } as const;
93
+
94
+ export const easings = {
95
+ standard: [0.4, 0, 0.2, 1] as [number, number, number, number],
96
+ decelerate: [0, 0, 0.2, 1] as [number, number, number, number],
97
+ accelerate: [0.4, 0, 1, 1] as [number, number, number, number],
98
+ bounce: [0.34, 1.56, 0.64, 1] as [number, number, number, number],
99
+ spring: [0.68, -0.55, 0.265, 1.55] as [number, number, number, number],
100
+ };
101
+ ```
102
+
103
+ > **Rule**: Never hardcode duration numbers or easing arrays in component files. Always import from `@/lib/design-tokens`.
104
+
105
+ ---
106
+
107
+ ## Section 3: Scroll Animations
108
+
109
+ Two approaches for triggering animations when elements enter the viewport:
110
+
111
+ ```tsx
112
+ 'use client';
113
+ import { motion, useInView } from 'motion/react';
114
+ import { useRef } from 'react';
115
+ import { fadeIn, slideInUp } from '@/motion/variants';
116
+
117
+ // Option A: whileInView (simpler, one-shot)
118
+ <motion.div
119
+ variants={slideInUp}
120
+ initial="hidden"
121
+ whileInView="visible"
122
+ viewport={{ once: true, margin: '-50px' }}
123
+ >
124
+
125
+ // Option B: useInView hook (more control)
126
+ function AnimatedSection({ children }: { children: React.ReactNode }) {
127
+ const ref = useRef(null);
128
+ const isInView = useInView(ref, { once: true, margin: '-100px' });
129
+ return (
130
+ <motion.div ref={ref} variants={fadeIn} initial="hidden" animate={isInView ? 'visible' : 'hidden'}>
131
+ {children}
132
+ </motion.div>
133
+ );
134
+ }
135
+ ```
136
+
137
+ - Use `once: true` for content sections — animations should not replay on scroll up.
138
+ - Adjust `margin` to fire slightly before the element fully enters the viewport.
139
+ - Prefer `whileInView` for simple cases; use `useInView` when you need imperative control or conditional logic.
140
+
141
+ ---
142
+
143
+ ## Section 4: Gesture Interactions
144
+
145
+ ```tsx
146
+ import { durations, easings } from '@/lib/design-tokens';
147
+
148
+ // Hover + tap on cards
149
+ <motion.div
150
+ whileHover={{ y: -4, boxShadow: 'var(--shadow-lift)' }}
151
+ whileTap={{ scale: 0.98 }}
152
+ transition={{ duration: durations.fast, ease: easings.standard }}
153
+ >
154
+
155
+ // Drag interaction
156
+ <motion.div
157
+ drag
158
+ dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }}
159
+ dragElastic={0.1}
160
+ whileDrag={{ scale: 1.05, cursor: 'grabbing' }}
161
+ >
162
+ ```
163
+
164
+ - `whileHover` and `whileTap` are inline — they do not need variant definitions for simple state changes.
165
+ - Use `var(--shadow-lift)` from the design system rather than hardcoded box-shadow values.
166
+ - `dragElastic={0.1}` gives a subtle rubber-band feel without being distracting.
167
+
168
+ ---
169
+
170
+ ## Section 5: AnimatePresence (Page Transitions)
171
+
172
+ Integrate `AnimatePresence` at the layout level to animate between routes:
173
+
174
+ ```tsx
175
+ // app/layout.tsx
176
+ 'use client';
177
+ import { AnimatePresence, motion } from 'motion/react';
178
+ import { usePathname } from 'next/navigation';
179
+ import { pageTransition } from '@/motion/variants';
180
+
181
+ export default function Layout({ children }: { children: React.ReactNode }) {
182
+ const pathname = usePathname();
183
+
184
+ return (
185
+ <html>
186
+ <body>
187
+ <AnimatePresence mode="wait">
188
+ <motion.main
189
+ key={pathname}
190
+ variants={pageTransition}
191
+ initial="hidden"
192
+ animate="visible"
193
+ exit="exit"
194
+ >
195
+ {children}
196
+ </motion.main>
197
+ </AnimatePresence>
198
+ </body>
199
+ </html>
200
+ );
201
+ }
202
+ ```
203
+
204
+ - `mode="wait"` ensures the exit animation completes before the entering page renders.
205
+ - The `key` prop must change on navigation — use `usePathname()` from `next/navigation`.
206
+ - Keep page transitions subtle (`y: 8`, fast duration) — aggressive transitions hurt perceived performance.
207
+
208
+ ---
209
+
210
+ ## Section 6: Loading Patterns
211
+
212
+ Prefer CSS `animate-pulse` for skeleton loaders over Framer Motion — CSS animations run off the main thread and have lower performance overhead.
213
+
214
+ ```tsx
215
+ // components/skeleton.tsx
216
+ import { cn } from '@/lib/utils';
217
+
218
+ function Skeleton({ className }: { className?: string }) {
219
+ return (
220
+ <div
221
+ className={cn(
222
+ 'animate-pulse rounded-[var(--radius-md)] bg-[var(--color-neutral-200)]',
223
+ className
224
+ )}
225
+ />
226
+ );
227
+ }
228
+
229
+ // Skeleton card example
230
+ function SkeletonCard() {
231
+ return (
232
+ <div className="p-[var(--space-6)] space-y-[var(--space-4)]">
233
+ <Skeleton className="h-4 w-3/4" />
234
+ <Skeleton className="h-4 w-1/2" />
235
+ <Skeleton className="h-20 w-full" />
236
+ </div>
237
+ );
238
+ }
239
+ ```
240
+
241
+ - Use design token CSS variables (`--radius-md`, `--color-neutral-200`, `--space-*`) — never hardcode dimensions or colors.
242
+ - Skeleton shapes should mirror the actual content layout to minimize layout shift on load.
243
+ - For spinner/progress indicators that need JS control, use a simple Framer Motion `animate={{ rotate: 360 }}` with `repeat: Infinity`.
244
+
245
+ ---
246
+
247
+ ## Section 7: WCAG Accessibility (MANDATORY)
248
+
249
+ > **This section is non-negotiable.** All animated components must respect `prefers-reduced-motion`.
250
+
251
+ ### Hook pattern — wrap all animated components
252
+
253
+ ```tsx
254
+ 'use client';
255
+ import { motion, useReducedMotion } from 'motion/react';
256
+ import { slideInUp } from '@/motion/variants';
257
+
258
+ function AnimatedCard({ children }: { children: React.ReactNode }) {
259
+ const shouldReduceMotion = useReducedMotion();
260
+
261
+ const variants = shouldReduceMotion
262
+ ? { hidden: { opacity: 0 }, visible: { opacity: 1 } } // instant, no movement
263
+ : slideInUp;
264
+
265
+ return (
266
+ <motion.div variants={variants} initial="hidden" animate="visible">
267
+ {children}
268
+ </motion.div>
269
+ );
270
+ }
271
+ ```
272
+
273
+ When `shouldReduceMotion` is `true`:
274
+ - Remove all `x`, `y`, `scale` transforms.
275
+ - Keep opacity transitions (they do not cause vestibular issues).
276
+ - Do not set `transition: { duration: 0 }` — a very short fade is acceptable and less jarring than an instant pop-in.
277
+
278
+ ### CSS fallback (add to global stylesheet)
279
+
280
+ ```css
281
+ @media (prefers-reduced-motion: reduce) {
282
+ *, *::before, *::after {
283
+ animation-duration: 0.01ms !important;
284
+ transition-duration: 0.01ms !important;
285
+ }
286
+ }
287
+ ```
288
+
289
+ This CSS fallback catches animations outside Framer Motion (Tailwind `animate-*`, third-party components, CSS keyframes).
290
+
291
+ ---
292
+
293
+ ## Section 8: Anti-patterns
294
+
295
+ | Anti-pattern | Correct approach |
296
+ |---|---|
297
+ | `duration: 0.8` for UI interactions | `duration: durations.fast` (0.15) for micro-interactions |
298
+ | Hardcoded easing `[0.4, 0, 0.2, 1]` | `ease: easings.standard` (from design-tokens) |
299
+ | No `useReducedMotion()` check | Always check or use CSS media query |
300
+ | Animating everything on the page | Reserve motion for 3-5 meaningful moments per page |
301
+ | Forgetting `animation-fill-mode: backwards` in stagger | Will cause a flash before the animation starts |
302
+ | `AnimatePresence` without `key` changes | Exit animations will never fire |
303
+ | Using `motion` for skeleton loaders | Use CSS `animate-pulse` — lower performance cost |
304
+ | `npm install framer-motion` / `import ... from 'framer-motion'` | `npm install motion` / `import ... from 'motion/react'` — `framer-motion` is the legacy name |
305
+ | Need ScrollTrigger, SVG morph, or complex timelines | Use GSAP — see `frontend/gsap/gsap-core.md` |
306
+
307
+ ---
308
+
309
+ ## Section 9: Installation
310
+
311
+ ```bash
312
+ npm install motion
313
+ ```
314
+
315
+ > The package is `motion` and components import from `motion/react`. The old
316
+ > `framer-motion` package still resolves as a deprecated alias, but new code
317
+ > should depend on `motion` directly. The `framer-motion` import name is only
318
+ > kept for projects running inside the Framer design tool.
319
+
320
+ Verify the installed version supports the features used here (requires motion >= 11):
321
+
322
+ ```bash
323
+ npm list motion
324
+ ```
325
+
326
+ ---
327
+
328
+ *MORPH-SPEC by Polymorphism Tech*