tribunal-kit 2.4.5 → 3.0.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 (144) hide show
  1. package/.agent/agents/accessibility-reviewer.md +220 -134
  2. package/.agent/agents/ai-code-reviewer.md +233 -129
  3. package/.agent/agents/backend-specialist.md +238 -178
  4. package/.agent/agents/code-archaeologist.md +181 -119
  5. package/.agent/agents/database-architect.md +207 -164
  6. package/.agent/agents/debugger.md +218 -151
  7. package/.agent/agents/dependency-reviewer.md +136 -55
  8. package/.agent/agents/devops-engineer.md +238 -175
  9. package/.agent/agents/documentation-writer.md +221 -137
  10. package/.agent/agents/explorer-agent.md +180 -142
  11. package/.agent/agents/frontend-reviewer.md +194 -80
  12. package/.agent/agents/frontend-specialist.md +237 -188
  13. package/.agent/agents/game-developer.md +52 -184
  14. package/.agent/agents/logic-reviewer.md +149 -78
  15. package/.agent/agents/mobile-developer.md +223 -152
  16. package/.agent/agents/mobile-reviewer.md +195 -79
  17. package/.agent/agents/orchestrator.md +211 -170
  18. package/.agent/agents/penetration-tester.md +174 -131
  19. package/.agent/agents/performance-optimizer.md +203 -139
  20. package/.agent/agents/performance-reviewer.md +211 -108
  21. package/.agent/agents/product-manager.md +162 -108
  22. package/.agent/agents/project-planner.md +162 -142
  23. package/.agent/agents/qa-automation-engineer.md +242 -138
  24. package/.agent/agents/security-auditor.md +194 -170
  25. package/.agent/agents/seo-specialist.md +213 -132
  26. package/.agent/agents/sql-reviewer.md +194 -73
  27. package/.agent/agents/supervisor-agent.md +203 -156
  28. package/.agent/agents/test-coverage-reviewer.md +193 -81
  29. package/.agent/agents/type-safety-reviewer.md +208 -65
  30. package/.agent/scripts/__pycache__/auto_preview.cpython-311.pyc +0 -0
  31. package/.agent/scripts/__pycache__/bundle_analyzer.cpython-311.pyc +0 -0
  32. package/.agent/scripts/__pycache__/checklist.cpython-311.pyc +0 -0
  33. package/.agent/scripts/__pycache__/dependency_analyzer.cpython-311.pyc +0 -0
  34. package/.agent/scripts/__pycache__/security_scan.cpython-311.pyc +0 -0
  35. package/.agent/scripts/__pycache__/session_manager.cpython-311.pyc +0 -0
  36. package/.agent/scripts/__pycache__/skill_integrator.cpython-311.pyc +0 -0
  37. package/.agent/scripts/__pycache__/swarm_dispatcher.cpython-311.pyc +0 -0
  38. package/.agent/scripts/__pycache__/test_runner.cpython-311.pyc +0 -0
  39. package/.agent/scripts/__pycache__/verify_all.cpython-311.pyc +0 -0
  40. package/.agent/skills/agent-organizer/SKILL.md +126 -132
  41. package/.agent/skills/ai-prompt-injection-defense/SKILL.md +160 -0
  42. package/.agent/skills/api-patterns/SKILL.md +289 -257
  43. package/.agent/skills/api-security-auditor/SKILL.md +177 -0
  44. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +1 -1
  45. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +1 -1
  46. package/.agent/skills/appflow-wireframe/SKILL.md +107 -58
  47. package/.agent/skills/architecture/SKILL.md +331 -200
  48. package/.agent/skills/authentication-best-practices/SKILL.md +173 -0
  49. package/.agent/skills/bash-linux/SKILL.md +154 -215
  50. package/.agent/skills/brainstorming/SKILL.md +104 -210
  51. package/.agent/skills/building-native-ui/SKILL.md +174 -0
  52. package/.agent/skills/clean-code/SKILL.md +360 -206
  53. package/.agent/skills/config-validator/SKILL.md +141 -165
  54. package/.agent/skills/csharp-developer/SKILL.md +528 -107
  55. package/.agent/skills/database-design/SKILL.md +455 -275
  56. package/.agent/skills/deployment-procedures/SKILL.md +145 -188
  57. package/.agent/skills/devops-engineer/SKILL.md +332 -134
  58. package/.agent/skills/devops-incident-responder/SKILL.md +113 -98
  59. package/.agent/skills/edge-computing/SKILL.md +157 -213
  60. package/.agent/skills/extract-design-system/SKILL.md +134 -0
  61. package/.agent/skills/framer-motion-expert/SKILL.md +939 -0
  62. package/.agent/skills/game-design-expert/SKILL.md +105 -0
  63. package/.agent/skills/game-engineering-expert/SKILL.md +122 -0
  64. package/.agent/skills/geo-fundamentals/SKILL.md +124 -215
  65. package/.agent/skills/github-operations/SKILL.md +314 -354
  66. package/.agent/skills/gsap-expert/SKILL.md +901 -0
  67. package/.agent/skills/i18n-localization/SKILL.md +138 -216
  68. package/.agent/skills/intelligent-routing/SKILL.md +127 -139
  69. package/.agent/skills/llm-engineering/SKILL.md +357 -258
  70. package/.agent/skills/local-first/SKILL.md +154 -203
  71. package/.agent/skills/mcp-builder/SKILL.md +118 -224
  72. package/.agent/skills/nextjs-react-expert/SKILL.md +783 -203
  73. package/.agent/skills/nodejs-best-practices/SKILL.md +559 -280
  74. package/.agent/skills/observability/SKILL.md +330 -285
  75. package/.agent/skills/parallel-agents/SKILL.md +122 -181
  76. package/.agent/skills/performance-profiling/SKILL.md +254 -197
  77. package/.agent/skills/plan-writing/SKILL.md +118 -188
  78. package/.agent/skills/platform-engineer/SKILL.md +123 -135
  79. package/.agent/skills/playwright-best-practices/SKILL.md +162 -0
  80. package/.agent/skills/powershell-windows/SKILL.md +146 -230
  81. package/.agent/skills/python-pro/SKILL.md +879 -114
  82. package/.agent/skills/react-specialist/SKILL.md +931 -108
  83. package/.agent/skills/readme-builder/SKILL.md +42 -0
  84. package/.agent/skills/realtime-patterns/SKILL.md +304 -296
  85. package/.agent/skills/rust-pro/SKILL.md +701 -240
  86. package/.agent/skills/seo-fundamentals/SKILL.md +154 -181
  87. package/.agent/skills/server-management/SKILL.md +190 -212
  88. package/.agent/skills/shadcn-ui-expert/SKILL.md +206 -0
  89. package/.agent/skills/skill-creator/SKILL.md +68 -0
  90. package/.agent/skills/sql-pro/SKILL.md +633 -104
  91. package/.agent/skills/supabase-postgres-best-practices/SKILL.md +78 -0
  92. package/.agent/skills/swiftui-expert/SKILL.md +176 -0
  93. package/.agent/skills/systematic-debugging/SKILL.md +118 -186
  94. package/.agent/skills/tailwind-patterns/SKILL.md +576 -232
  95. package/.agent/skills/tdd-workflow/SKILL.md +137 -209
  96. package/.agent/skills/testing-patterns/SKILL.md +573 -205
  97. package/.agent/skills/vue-expert/SKILL.md +964 -119
  98. package/.agent/skills/vulnerability-scanner/SKILL.md +269 -316
  99. package/.agent/skills/web-accessibility-auditor/SKILL.md +193 -0
  100. package/.agent/skills/webapp-testing/SKILL.md +145 -236
  101. package/.agent/workflows/api-tester.md +151 -279
  102. package/.agent/workflows/audit.md +138 -168
  103. package/.agent/workflows/brainstorm.md +110 -146
  104. package/.agent/workflows/changelog.md +112 -144
  105. package/.agent/workflows/create.md +124 -139
  106. package/.agent/workflows/debug.md +189 -196
  107. package/.agent/workflows/deploy.md +189 -153
  108. package/.agent/workflows/enhance.md +151 -139
  109. package/.agent/workflows/fix.md +135 -143
  110. package/.agent/workflows/generate.md +157 -164
  111. package/.agent/workflows/migrate.md +160 -163
  112. package/.agent/workflows/orchestrate.md +168 -151
  113. package/.agent/workflows/performance-benchmarker.md +123 -305
  114. package/.agent/workflows/plan.md +173 -151
  115. package/.agent/workflows/preview.md +80 -137
  116. package/.agent/workflows/refactor.md +183 -153
  117. package/.agent/workflows/review-ai.md +129 -140
  118. package/.agent/workflows/review.md +116 -155
  119. package/.agent/workflows/session.md +94 -154
  120. package/.agent/workflows/status.md +79 -125
  121. package/.agent/workflows/strengthen-skills.md +139 -99
  122. package/.agent/workflows/swarm.md +179 -194
  123. package/.agent/workflows/test.md +211 -166
  124. package/.agent/workflows/tribunal-backend.md +113 -111
  125. package/.agent/workflows/tribunal-database.md +115 -132
  126. package/.agent/workflows/tribunal-frontend.md +118 -115
  127. package/.agent/workflows/tribunal-full.md +133 -136
  128. package/.agent/workflows/tribunal-mobile.md +119 -123
  129. package/.agent/workflows/tribunal-performance.md +133 -152
  130. package/.agent/workflows/ui-ux-pro-max.md +143 -171
  131. package/README.md +11 -15
  132. package/package.json +1 -1
  133. package/.agent/skills/dotnet-core-expert/SKILL.md +0 -103
  134. package/.agent/skills/game-development/2d-games/SKILL.md +0 -119
  135. package/.agent/skills/game-development/3d-games/SKILL.md +0 -135
  136. package/.agent/skills/game-development/SKILL.md +0 -236
  137. package/.agent/skills/game-development/game-art/SKILL.md +0 -185
  138. package/.agent/skills/game-development/game-audio/SKILL.md +0 -190
  139. package/.agent/skills/game-development/game-design/SKILL.md +0 -129
  140. package/.agent/skills/game-development/mobile-games/SKILL.md +0 -108
  141. package/.agent/skills/game-development/multiplayer/SKILL.md +0 -132
  142. package/.agent/skills/game-development/pc-games/SKILL.md +0 -144
  143. package/.agent/skills/game-development/vr-ar/SKILL.md +0 -123
  144. package/.agent/skills/game-development/web-games/SKILL.md +0 -150
@@ -0,0 +1,939 @@
1
+ ---
2
+ name: framer-motion-expert
3
+ description: Framer Motion 12+ mastery for React. Declarative animations, layout transitions, gestures, scroll-linked motion, AnimatePresence, useAnimate, useMotionValue, LazyMotion bundle optimization, and accessibility (prefers-reduced-motion). Use when building React component animations, page transitions, shared layout animations, or gesture-driven UI.
4
+ allowed-tools: Read, Write, Edit, Glob, Grep
5
+ version: 1.0.0
6
+ last-updated: 2026-03-30
7
+ applies-to-model: gemini-2.5-pro, claude-3-7-sonnet
8
+ ---
9
+
10
+ # Framer Motion Expert — React Animation Library
11
+
12
+ > Framer Motion is declarative-first. If you're writing imperative animation loops in React, you're fighting the framework.
13
+ > Every `motion.div` must have a reason. Every `AnimatePresence` must have a `key`. Every `useMotionValue` must avoid re-renders.
14
+
15
+ ---
16
+
17
+ ## Core API — Declarative Animations
18
+
19
+ ### The `motion` Component
20
+
21
+ ```tsx
22
+ import { motion } from "framer-motion";
23
+
24
+ // motion.div, motion.span, motion.button, motion.svg, motion.path, etc.
25
+ // Any HTML or SVG element can be prefixed with `motion.`
26
+
27
+ function FadeInBox() {
28
+ return (
29
+ <motion.div
30
+ initial={{ opacity: 0, y: 20 }}
31
+ animate={{ opacity: 1, y: 0 }}
32
+ transition={{ duration: 0.5, ease: "easeOut" }}
33
+ >
34
+ Hello, animated world
35
+ </motion.div>
36
+ );
37
+ }
38
+
39
+ // ❌ HALLUCINATION TRAP: There is NO <Motion> component (capital M)
40
+ // ❌ HALLUCINATION TRAP: There is NO motion() function wrapper
41
+ // ✅ It's always motion.div, motion.span, etc. (lowercase dot notation)
42
+ ```
43
+
44
+ ### The `animate` Prop
45
+
46
+ ```tsx
47
+ // Object syntax (most common)
48
+ <motion.div animate={{ x: 100, opacity: 1 }} />
49
+
50
+ // Dynamic — responds to state changes automatically
51
+ const [isOpen, setIsOpen] = useState(false);
52
+ <motion.div animate={{ height: isOpen ? "auto" : 0 }} />
53
+
54
+ // ❌ HALLUCINATION TRAP: `animate={{ height: "auto" }}` only works
55
+ // in Framer Motion 11+ with the layout animation engine.
56
+ // For older versions, use explicit pixel values or the `layout` prop.
57
+ ```
58
+
59
+ ### `initial`, `animate`, `exit`
60
+
61
+ ```tsx
62
+ <AnimatePresence>
63
+ {isVisible && (
64
+ <motion.div
65
+ key="modal" // ← key is REQUIRED inside AnimatePresence
66
+ initial={{ opacity: 0, scale: 0.95 }}
67
+ animate={{ opacity: 1, scale: 1 }}
68
+ exit={{ opacity: 0, scale: 0.95 }}
69
+ transition={{ duration: 0.3 }}
70
+ >
71
+ Modal content
72
+ </motion.div>
73
+ )}
74
+ </AnimatePresence>
75
+
76
+ // ❌ HALLUCINATION TRAP: exit animations do NOT work without AnimatePresence
77
+ // ❌ HALLUCINATION TRAP: AnimatePresence children MUST have a unique `key`
78
+ ```
79
+
80
+ ### Variants (Declarative Animation Maps)
81
+
82
+ ```tsx
83
+ const containerVariants = {
84
+ hidden: { opacity: 0 },
85
+ visible: {
86
+ opacity: 1,
87
+ transition: {
88
+ staggerChildren: 0.1, // stagger between children
89
+ delayChildren: 0.2, // delay before first child
90
+ },
91
+ },
92
+ };
93
+
94
+ const itemVariants = {
95
+ hidden: { opacity: 0, y: 20 },
96
+ visible: { opacity: 1, y: 0 },
97
+ };
98
+
99
+ function List() {
100
+ return (
101
+ <motion.ul
102
+ variants={containerVariants}
103
+ initial="hidden"
104
+ animate="visible"
105
+ >
106
+ {items.map((item) => (
107
+ <motion.li key={item.id} variants={itemVariants}>
108
+ {item.name}
109
+ </motion.li>
110
+ ))}
111
+ </motion.ul>
112
+ );
113
+ }
114
+
115
+ // Variant names propagate automatically to children
116
+ // Children inherit the current variant name from parent
117
+ // You do NOT need to set initial/animate on each child
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Transitions
123
+
124
+ ### Tween (Default)
125
+
126
+ ```tsx
127
+ <motion.div
128
+ animate={{ x: 100 }}
129
+ transition={{
130
+ type: "tween", // default for most properties
131
+ duration: 0.5,
132
+ ease: "easeInOut", // or [0.42, 0, 0.58, 1] (cubic-bezier)
133
+ delay: 0.2,
134
+ repeat: 2, // number of repeats (Infinity for loop)
135
+ repeatType: "reverse", // "loop", "reverse", "mirror"
136
+ repeatDelay: 0.5,
137
+ }}
138
+ />
139
+ ```
140
+
141
+ ### Spring (Physics-Based)
142
+
143
+ ```tsx
144
+ <motion.div
145
+ animate={{ x: 100 }}
146
+ transition={{
147
+ type: "spring",
148
+ stiffness: 300, // spring tension (default: 100)
149
+ damping: 20, // resistance (default: 10)
150
+ mass: 1, // weight (default: 1)
151
+ bounce: 0.25, // shorthand: 0 = no bounce, 1 = max bounce
152
+ // duration + bounce is an alternative to stiffness + damping
153
+ duration: 0.8, // approximate duration (auto-calculates stiffness/damping)
154
+ }}
155
+ />
156
+
157
+ // ❌ HALLUCINATION TRAP: You cannot use BOTH stiffness+damping AND duration+bounce
158
+ // Pick one pair. Using both causes unpredictable behavior.
159
+ ```
160
+
161
+ ### Inertia
162
+
163
+ ```tsx
164
+ // Inertia transitions are used after drag gestures
165
+ <motion.div
166
+ drag="x"
167
+ dragTransition={{
168
+ bounceStiffness: 600,
169
+ bounceDamping: 20,
170
+ power: 0.8, // deceleration rate
171
+ timeConstant: 750, // ms to reach ~63% of projected distance
172
+ }}
173
+ />
174
+ ```
175
+
176
+ ### Orchestration
177
+
178
+ ```tsx
179
+ // Parent controls when children animate
180
+ const parent = {
181
+ visible: {
182
+ transition: {
183
+ when: "beforeChildren", // "afterChildren", "beforeChildren"
184
+ staggerChildren: 0.1,
185
+ staggerDirection: 1, // 1 = forward, -1 = reverse
186
+ delayChildren: 0.3,
187
+ },
188
+ },
189
+ };
190
+ ```
191
+
192
+ ### Per-Property Transitions
193
+
194
+ ```tsx
195
+ <motion.div
196
+ animate={{ x: 100, opacity: 1 }}
197
+ transition={{
198
+ x: { type: "spring", stiffness: 300 },
199
+ opacity: { duration: 0.2 },
200
+ default: { duration: 0.5 }, // fallback for unlisted properties
201
+ }}
202
+ />
203
+ ```
204
+
205
+ ---
206
+
207
+ ## Gestures
208
+
209
+ ### Hover, Tap, Focus
210
+
211
+ ```tsx
212
+ <motion.button
213
+ whileHover={{ scale: 1.05, backgroundColor: "#4338ca" }}
214
+ whileTap={{ scale: 0.95 }}
215
+ whileFocus={{ boxShadow: "0 0 0 3px rgba(66, 153, 225, 0.6)" }}
216
+ transition={{ type: "spring", stiffness: 400, damping: 15 }}
217
+ >
218
+ Click me
219
+ </motion.button>
220
+ ```
221
+
222
+ ### Drag
223
+
224
+ ```tsx
225
+ <motion.div
226
+ drag // "x", "y", or true for both axes
227
+ dragConstraints={{ left: -100, right: 100, top: -50, bottom: 50 }}
228
+ dragElastic={0.2} // 0 = hard stop, 1 = free (default: 0.35)
229
+ dragMomentum={true} // continue with inertia after release (default: true)
230
+ dragSnapToOrigin // return to starting position on release
231
+ onDragStart={(event, info) => console.log(info.point)}
232
+ onDrag={(event, info) => console.log(info.offset)}
233
+ onDragEnd={(event, info) => console.log(info.velocity)}
234
+ />
235
+
236
+ // Drag within a parent container
237
+ function DragInContainer() {
238
+ const constraintsRef = useRef(null);
239
+ return (
240
+ <motion.div ref={constraintsRef} style={{ width: 400, height: 400 }}>
241
+ <motion.div drag dragConstraints={constraintsRef} />
242
+ </motion.div>
243
+ );
244
+ }
245
+ ```
246
+
247
+ ### `whileInView` (Scroll-Triggered)
248
+
249
+ ```tsx
250
+ <motion.div
251
+ initial={{ opacity: 0, y: 50 }}
252
+ whileInView={{ opacity: 1, y: 0 }}
253
+ viewport={{
254
+ once: true, // only animate once (do NOT re-trigger on scroll back)
255
+ amount: 0.3, // 30% of element must be visible
256
+ margin: "-100px", // shrink viewport detection area
257
+ }}
258
+ transition={{ duration: 0.6 }}
259
+ >
260
+ Appears on scroll
261
+ </motion.div>
262
+
263
+ // ❌ HALLUCINATION TRAP: `viewport.once` defaults to false
264
+ // This means the animation replays every time the element enters/exits
265
+ // Most designs want `once: true` for entrance animations
266
+ ```
267
+
268
+ ---
269
+
270
+ ## Layout Animations
271
+
272
+ ### The `layout` Prop
273
+
274
+ ```tsx
275
+ // The MAGIC of Framer Motion — automatic layout animations
276
+ // When a component's size or position changes,
277
+ // Framer Motion animates between the old and new layout automatically
278
+
279
+ function ExpandableCard({ isExpanded }) {
280
+ return (
281
+ <motion.div
282
+ layout // ← THIS is the magic prop
283
+ style={{
284
+ width: isExpanded ? 400 : 200,
285
+ height: isExpanded ? 300 : 100,
286
+ }}
287
+ transition={{ type: "spring", stiffness: 200, damping: 25 }}
288
+ >
289
+ <motion.p layout="position">
290
+ {/* layout="position" — only animate position, not size */}
291
+ Card content
292
+ </motion.p>
293
+ </motion.div>
294
+ );
295
+ }
296
+
297
+ // layout values:
298
+ // true — animate both position and size
299
+ // "position" — only animate position (prevents text reflow flicker)
300
+ // "size" — only animate size
301
+ // "preserve-aspect" — maintain aspect ratio during transition
302
+ ```
303
+
304
+ ### `layoutId` — Shared Element Transitions
305
+
306
+ ```tsx
307
+ // The most powerful feature in Framer Motion
308
+ // Elements with the same layoutId across renders morph into each other
309
+
310
+ function ItemList({ selectedId, onSelect }) {
311
+ return (
312
+ <div className="grid">
313
+ {items.map((item) => (
314
+ <motion.div
315
+ key={item.id}
316
+ layoutId={`card-${item.id}`}
317
+ onClick={() => onSelect(item.id)}
318
+ >
319
+ <motion.h2 layoutId={`title-${item.id}`}>{item.title}</motion.h2>
320
+ </motion.div>
321
+ ))}
322
+
323
+ <AnimatePresence>
324
+ {selectedId && (
325
+ <motion.div
326
+ layoutId={`card-${selectedId}`}
327
+ className="expanded-card"
328
+ >
329
+ <motion.h2 layoutId={`title-${selectedId}`}>
330
+ {items.find(i => i.id === selectedId).title}
331
+ </motion.h2>
332
+ <motion.p
333
+ initial={{ opacity: 0 }}
334
+ animate={{ opacity: 1 }}
335
+ exit={{ opacity: 0 }}
336
+ >
337
+ Expanded content...
338
+ </motion.p>
339
+ </motion.div>
340
+ )}
341
+ </AnimatePresence>
342
+ </div>
343
+ );
344
+ }
345
+
346
+ // ❌ HALLUCINATION TRAP: layoutId elements MUST be in the same LayoutGroup
347
+ // or be siblings under the same AnimatePresence. Cross-tree layoutId
348
+ // requires wrapping in <LayoutGroup>.
349
+ ```
350
+
351
+ ### `LayoutGroup`
352
+
353
+ ```tsx
354
+ import { LayoutGroup } from "framer-motion";
355
+
356
+ // Required when layoutId elements span different component trees
357
+ <LayoutGroup>
358
+ <Sidebar />
359
+ <MainContent />
360
+ </LayoutGroup>
361
+
362
+ // Also useful to prevent layout animations from affecting siblings
363
+ <LayoutGroup id="sidebar">
364
+ {/* Layout animations here won't leak to the rest of the page */}
365
+ </LayoutGroup>
366
+ ```
367
+
368
+ ---
369
+
370
+ ## Scroll Animations
371
+
372
+ ### `useScroll`
373
+
374
+ ```tsx
375
+ import { motion, useScroll, useTransform } from "framer-motion";
376
+
377
+ function ParallaxHero() {
378
+ const { scrollYProgress } = useScroll();
379
+
380
+ // Map scroll progress (0–1) to a y offset (-50 to 50)
381
+ const y = useTransform(scrollYProgress, [0, 1], [0, -200]);
382
+ const opacity = useTransform(scrollYProgress, [0, 0.5], [1, 0]);
383
+
384
+ return (
385
+ <motion.div style={{ y, opacity }}>
386
+ <h1>Parallax Hero</h1>
387
+ </motion.div>
388
+ );
389
+ }
390
+ ```
391
+
392
+ ### Element-Scoped Scroll Tracking
393
+
394
+ ```tsx
395
+ function ProgressBar() {
396
+ const ref = useRef(null);
397
+ const { scrollYProgress } = useScroll({
398
+ target: ref, // track this element's scroll position
399
+ offset: ["start end", "end start"], // [trigger start, trigger end]
400
+ });
401
+
402
+ return (
403
+ <div ref={ref}>
404
+ <motion.div
405
+ style={{ scaleX: scrollYProgress, transformOrigin: "left" }}
406
+ className="progress-bar"
407
+ />
408
+ Long content...
409
+ </div>
410
+ );
411
+ }
412
+ ```
413
+
414
+ ### `useTransform` Chains
415
+
416
+ ```tsx
417
+ const { scrollYProgress } = useScroll();
418
+
419
+ // Chain multiple transforms
420
+ const scale = useTransform(scrollYProgress, [0, 0.5, 1], [1, 1.5, 1]);
421
+ const rotate = useTransform(scrollYProgress, [0, 1], [0, 360]);
422
+ const color = useTransform(
423
+ scrollYProgress,
424
+ [0, 0.5, 1],
425
+ ["#ff0000", "#00ff00", "#0000ff"]
426
+ );
427
+
428
+ // Derived transforms
429
+ const invertedY = useTransform(scrollYProgress, (v) => 1 - v);
430
+ ```
431
+
432
+ ---
433
+
434
+ ## Hooks
435
+
436
+ ### `useAnimate` (Imperative Control)
437
+
438
+ ```tsx
439
+ import { useAnimate, stagger } from "framer-motion";
440
+
441
+ function AnimatedList() {
442
+ const [scope, animate] = useAnimate();
443
+
444
+ async function handleClick() {
445
+ // Imperative sequence
446
+ await animate(".list-item", { opacity: 1, y: 0 }, {
447
+ delay: stagger(0.1),
448
+ duration: 0.3,
449
+ });
450
+
451
+ await animate(".cta-button", { scale: [1, 1.1, 1] }, {
452
+ duration: 0.4,
453
+ });
454
+ }
455
+
456
+ return (
457
+ <div ref={scope}>
458
+ {items.map((item) => (
459
+ <div key={item.id} className="list-item" style={{ opacity: 0 }}>
460
+ {item.name}
461
+ </div>
462
+ ))}
463
+ <button className="cta-button" onClick={handleClick}>
464
+ Show All
465
+ </button>
466
+ </div>
467
+ );
468
+ }
469
+
470
+ // ❌ HALLUCINATION TRAP: useAnimate returns [scope, animate]
471
+ // NOT [ref, controls] — that was the old useCycle/useAnimationControls API
472
+ ```
473
+
474
+ ### `useMotionValue`
475
+
476
+ ```tsx
477
+ import { motion, useMotionValue, useTransform } from "framer-motion";
478
+
479
+ function RotatingCard() {
480
+ const x = useMotionValue(0);
481
+ const rotateY = useTransform(x, [-200, 200], [-45, 45]);
482
+ const background = useTransform(
483
+ x,
484
+ [-200, 0, 200],
485
+ ["#ff008c", "#7700ff", "#00d4ff"]
486
+ );
487
+
488
+ return (
489
+ <motion.div
490
+ style={{ x, rotateY, background }}
491
+ drag="x"
492
+ dragConstraints={{ left: -200, right: 200 }}
493
+ />
494
+ );
495
+ }
496
+
497
+ // ✅ useMotionValue does NOT trigger React re-renders
498
+ // This is the key performance advantage over useState for animations
499
+ ```
500
+
501
+ ### `useSpring`
502
+
503
+ ```tsx
504
+ import { useSpring, useMotionValue } from "framer-motion";
505
+
506
+ const x = useMotionValue(0);
507
+ const springX = useSpring(x, {
508
+ stiffness: 300,
509
+ damping: 30,
510
+ restDelta: 0.001, // stop spring when movement is below this
511
+ });
512
+
513
+ // springX automatically follows x with spring physics
514
+ // Use springX in style={{ x: springX }} for smooth following
515
+ ```
516
+
517
+ ### `useVelocity`
518
+
519
+ ```tsx
520
+ import { useMotionValue, useVelocity, useTransform } from "framer-motion";
521
+
522
+ const x = useMotionValue(0);
523
+ const xVelocity = useVelocity(x);
524
+ const skewX = useTransform(xVelocity, [-1000, 0, 1000], [-15, 0, 15]);
525
+
526
+ // Skews element based on drag speed — creates a "rubber" feel
527
+ <motion.div style={{ x, skewX }} drag="x" />
528
+ ```
529
+
530
+ ---
531
+
532
+ ## AnimatePresence (Mount/Unmount Animations)
533
+
534
+ ```tsx
535
+ import { AnimatePresence, motion } from "framer-motion";
536
+
537
+ function Notifications({ items }) {
538
+ return (
539
+ <AnimatePresence
540
+ mode="sync" // "sync" | "wait" | "popLayout"
541
+ initial={false} // skip initial animation on first render
542
+ >
543
+ {items.map((item) => (
544
+ <motion.div
545
+ key={item.id} // ← UNIQUE KEY IS MANDATORY
546
+ initial={{ opacity: 0, height: 0 }}
547
+ animate={{ opacity: 1, height: "auto" }}
548
+ exit={{ opacity: 0, height: 0 }}
549
+ transition={{ duration: 0.3 }}
550
+ >
551
+ {item.message}
552
+ </motion.div>
553
+ ))}
554
+ </AnimatePresence>
555
+ );
556
+ }
557
+
558
+ // Modes:
559
+ // "sync" — new and old animate simultaneously (default)
560
+ // "wait" — wait for exit to finish before entering
561
+ // "popLayout" — uses FLIP to handle layout shifts during exit
562
+
563
+ // ❌ HALLUCINATION TRAP: `mode="wait"` used to be called `exitBeforeEnter`
564
+ // exitBeforeEnter was REMOVED in Framer Motion 7+
565
+ ```
566
+
567
+ ### Page Transitions (Next.js App Router)
568
+
569
+ ```tsx
570
+ // layout.tsx
571
+ "use client";
572
+ import { AnimatePresence } from "framer-motion";
573
+
574
+ export default function Layout({ children }: { children: React.ReactNode }) {
575
+ return (
576
+ <AnimatePresence mode="wait">
577
+ {children}
578
+ </AnimatePresence>
579
+ );
580
+ }
581
+
582
+ // page.tsx
583
+ "use client";
584
+ import { motion } from "framer-motion";
585
+
586
+ export default function Page() {
587
+ return (
588
+ <motion.main
589
+ initial={{ opacity: 0, y: 20 }}
590
+ animate={{ opacity: 1, y: 0 }}
591
+ exit={{ opacity: 0, y: -20 }}
592
+ transition={{ duration: 0.3 }}
593
+ >
594
+ Page content
595
+ </motion.main>
596
+ );
597
+ }
598
+ ```
599
+
600
+ ---
601
+
602
+ ## Performance & Accessibility
603
+
604
+ ### `m` vs `motion` — Bundle Optimization (LazyMotion)
605
+
606
+ ```tsx
607
+ import { LazyMotion, domAnimation, m } from "framer-motion";
608
+
609
+ // LazyMotion + domAnimation = ~5KB instead of ~30KB
610
+ // Use `m.div` instead of `motion.div` inside LazyMotion
611
+
612
+ function App() {
613
+ return (
614
+ <LazyMotion features={domAnimation}>
615
+ <m.div
616
+ initial={{ opacity: 0 }}
617
+ animate={{ opacity: 1 }}
618
+ >
619
+ Lightweight animation
620
+ </m.div>
621
+ </LazyMotion>
622
+ );
623
+ }
624
+
625
+ // For full feature set (layout animations, drag, etc.):
626
+ import { domMax } from "framer-motion";
627
+ <LazyMotion features={domMax}>
628
+
629
+ // ❌ HALLUCINATION TRAP: m.div does NOT work without LazyMotion wrapper
630
+ // ❌ HALLUCINATION TRAP: layout animations require domMax, not domAnimation
631
+ ```
632
+
633
+ ### `prefers-reduced-motion` (Accessibility — Mandatory)
634
+
635
+ ```tsx
636
+ import { useReducedMotion } from "framer-motion";
637
+
638
+ function AnimatedComponent() {
639
+ const shouldReduceMotion = useReducedMotion();
640
+
641
+ return (
642
+ <motion.div
643
+ animate={{
644
+ x: shouldReduceMotion ? 0 : 100,
645
+ opacity: 1, // opacity changes are always safe
646
+ }}
647
+ transition={{
648
+ duration: shouldReduceMotion ? 0 : 0.5,
649
+ }}
650
+ >
651
+ Accessible animation
652
+ </motion.div>
653
+ );
654
+ }
655
+
656
+ // ✅ RULE: Opacity and color transitions are acceptable for reduced-motion users
657
+ // ❌ RULE: Position, scale, and rotation animations must be disabled or minimized
658
+ // ❌ RULE: Auto-playing looping animations MUST stop under reduced motion
659
+ ```
660
+
661
+ ### Performance Rules
662
+
663
+ ```
664
+ ✅ Use useMotionValue instead of useState for animation-driven values
665
+ → useMotionValue does NOT trigger React re-renders
666
+
667
+ ✅ Use useTransform to derive values from motion values
668
+ → Avoids recomputation in the React render cycle
669
+
670
+ ✅ Animate transform properties (x, y, scale, rotate, opacity)
671
+ → These are GPU-composited and do not trigger layout
672
+
673
+ ✅ Use LazyMotion + m.div in production to reduce bundle size
674
+ → Cuts Framer Motion from ~30KB to ~5KB
675
+
676
+ ❌ Do NOT animate width, height, top, left, padding, margin
677
+ → These trigger layout recalculation every frame
678
+
679
+ ❌ Do NOT create new motion values inside render
680
+ → Causes GC pressure and breaks animation continuity
681
+
682
+ ❌ Do NOT nest AnimatePresence unnecessarily
683
+ → Each instance adds overhead to the reconciler
684
+ ```
685
+
686
+ ---
687
+
688
+ ## Framework Considerations
689
+
690
+ ### Next.js (App Router)
691
+
692
+ ```tsx
693
+ // Framer Motion components MUST be client components
694
+ "use client"; // ← required at top of file
695
+
696
+ import { motion } from "framer-motion";
697
+
698
+ // ❌ HALLUCINATION TRAP: motion.div CANNOT be used in Server Components
699
+ // The `motion` component requires browser APIs (DOM, window, RAF)
700
+
701
+ // For server-rendered pages with client animations,
702
+ // split into a server-rendered layout + client animation wrapper
703
+ ```
704
+
705
+ ### Cleanup on Unmount
706
+
707
+ ```tsx
708
+ // AnimatePresence handles unmount animations automatically
709
+ // But if using useAnimate or manual animation controls:
710
+
711
+ useEffect(() => {
712
+ const controls = animate(".element", { opacity: 1 });
713
+
714
+ return () => {
715
+ controls.stop(); // ← ALWAYS stop animations on cleanup
716
+ };
717
+ }, []);
718
+ ```
719
+
720
+ ### Vue / Non-React Usage
721
+
722
+ ```
723
+ ⚠️ Framer Motion is React-ONLY.
724
+
725
+ For Vue, use:
726
+ - @vueuse/motion (Vue 3 motion library, similar API)
727
+ - vue-kinesis (gesture-based)
728
+
729
+ For Svelte, use:
730
+ - svelte/animate and svelte/transition (built-in)
731
+ - Motion One (framework-agnostic, by Framer Motion creator)
732
+
733
+ For vanilla JS, use:
734
+ - Motion One (motion.dev) — from the same team, but framework-agnostic
735
+ - GSAP — use the gsap-expert skill instead
736
+
737
+ // ❌ HALLUCINATION TRAP: There is NO "framer-motion/vue" or "framer-motion/svelte"
738
+ // Framer Motion is exclusively a React library
739
+ ```
740
+
741
+ ---
742
+
743
+ ## Common Animation Patterns
744
+
745
+ ### Staggered List Entrance
746
+
747
+ ```tsx
748
+ const container = {
749
+ hidden: {},
750
+ visible: {
751
+ transition: { staggerChildren: 0.08, delayChildren: 0.1 },
752
+ },
753
+ };
754
+
755
+ const item = {
756
+ hidden: { opacity: 0, y: 20, filter: "blur(4px)" },
757
+ visible: {
758
+ opacity: 1,
759
+ y: 0,
760
+ filter: "blur(0px)",
761
+ transition: { duration: 0.4, ease: [0.25, 0.46, 0.45, 0.94] },
762
+ },
763
+ };
764
+
765
+ <motion.ul variants={container} initial="hidden" animate="visible">
766
+ {list.map((entry) => (
767
+ <motion.li key={entry.id} variants={item}>
768
+ {entry.name}
769
+ </motion.li>
770
+ ))}
771
+ </motion.ul>
772
+ ```
773
+
774
+ ### Smooth Tab Indicator
775
+
776
+ ```tsx
777
+ function Tabs({ tabs, activeTab }) {
778
+ return (
779
+ <div className="tab-list">
780
+ {tabs.map((tab) => (
781
+ <button
782
+ key={tab.id}
783
+ onClick={() => setActiveTab(tab.id)}
784
+ className="tab"
785
+ >
786
+ {tab.label}
787
+ {activeTab === tab.id && (
788
+ <motion.div
789
+ layoutId="tab-indicator" // ← shared element
790
+ className="tab-underline"
791
+ transition={{ type: "spring", stiffness: 500, damping: 30 }}
792
+ />
793
+ )}
794
+ </button>
795
+ ))}
796
+ </div>
797
+ );
798
+ }
799
+ ```
800
+
801
+ ### Card Hover Effect
802
+
803
+ ```tsx
804
+ <motion.div
805
+ whileHover={{ y: -4, boxShadow: "0 20px 40px rgba(0,0,0,0.15)" }}
806
+ transition={{ type: "spring", stiffness: 300, damping: 20 }}
807
+ className="card"
808
+ >
809
+ <motion.img
810
+ whileHover={{ scale: 1.03 }}
811
+ transition={{ duration: 0.3 }}
812
+ src={imageUrl}
813
+ />
814
+ </motion.div>
815
+ ```
816
+
817
+ ### Expandable Accordion
818
+
819
+ ```tsx
820
+ function Accordion({ title, children, isOpen, onToggle }) {
821
+ return (
822
+ <div>
823
+ <button onClick={onToggle}>{title}</button>
824
+ <AnimatePresence initial={false}>
825
+ {isOpen && (
826
+ <motion.div
827
+ key="content"
828
+ initial={{ height: 0, opacity: 0 }}
829
+ animate={{ height: "auto", opacity: 1 }}
830
+ exit={{ height: 0, opacity: 0 }}
831
+ transition={{ duration: 0.3, ease: [0.04, 0.62, 0.23, 0.98] }}
832
+ style={{ overflow: "hidden" }}
833
+ >
834
+ {children}
835
+ </motion.div>
836
+ )}
837
+ </AnimatePresence>
838
+ </div>
839
+ );
840
+ }
841
+ ```
842
+
843
+ ### Scroll Progress Bar
844
+
845
+ ```tsx
846
+ function ScrollProgressBar() {
847
+ const { scrollYProgress } = useScroll();
848
+
849
+ return (
850
+ <motion.div
851
+ style={{
852
+ scaleX: scrollYProgress,
853
+ transformOrigin: "left",
854
+ position: "fixed",
855
+ top: 0,
856
+ left: 0,
857
+ right: 0,
858
+ height: 4,
859
+ background: "linear-gradient(90deg, #06b6d4, #8b5cf6)",
860
+ zIndex: 50,
861
+ }}
862
+ />
863
+ );
864
+ }
865
+ ```
866
+
867
+ ---
868
+
869
+ ## Output Format
870
+
871
+ When this skill produces or reviews code, structure your output as follows:
872
+
873
+ ```
874
+ ━━━ Framer Motion Expert Report ━━━━━━━━━━━━━━━━━━━━━━━━
875
+ Skill: Framer Motion Expert
876
+ FM Version: 12+
877
+ Scope: [N files · N components]
878
+ ─────────────────────────────────────────────────
879
+ ✅ Passed: [checks that passed, or "All clean"]
880
+ ⚠️ Warnings: [non-blocking issues, or "None"]
881
+ ❌ Blocked: [blocking issues requiring fix, or "None"]
882
+ ─────────────────────────────────────────────────
883
+ VBC status: PENDING → VERIFIED
884
+ Evidence: [test output / lint pass / compile success]
885
+ ```
886
+
887
+ **VBC (Verification-Before-Completion) is mandatory.**
888
+ Do not mark status as VERIFIED until concrete terminal evidence is provided.
889
+
890
+ ---
891
+
892
+ ## 🤖 LLM-Specific Traps
893
+
894
+ AI coding assistants often fall into specific bad habits when generating Framer Motion code. These are strictly forbidden:
895
+
896
+ 1. **Inventing Component Names:** There is no `<Motion>`, `<MotionDiv>`, `<AnimatedDiv>`, or `motion()` wrapper function. The correct API is `motion.div`, `motion.span`, etc. (lowercase dot notation).
897
+ 2. **Using Deprecated `exitBeforeEnter`:** This prop was removed in Framer Motion 7+. The correct replacement is `mode="wait"` on `AnimatePresence`.
898
+ 3. **Missing `key` in AnimatePresence:** Every direct child of `AnimatePresence` MUST have a unique `key` prop for exit animations to work.
899
+ 4. **Using `motion.div` in Server Components:** Framer Motion requires browser APIs. Components using `motion` MUST be marked `"use client"` in Next.js App Router.
900
+ 5. **Confusing `useAnimation` (removed) with `useAnimate`:** The modern imperative API is `useAnimate()` which returns `[scope, animate]`, NOT `useAnimation()` which returned animation controls.
901
+ 6. **Using `m.div` Without `LazyMotion`:** The `m` component is only valid inside a `<LazyMotion>` provider. Using it standalone produces no animations.
902
+ 7. **Layout Animations Without `domMax`:** The `layout` prop and `layoutId` require `domMax` features in LazyMotion, not `domAnimation`.
903
+ 8. **Over-Animating:** Adding spring animations to every element creates visual chaos. Be intentional — every animation must serve a UX purpose.
904
+ 9. **Hallucinating Non-React Exports:** There is no `framer-motion/vue`, `framer-motion/svelte`, or `framer-motion/vanilla`. It is React-only.
905
+ 10. **Animating Layout Properties:** Animating `width`, `height`, `top`, `left` directly instead of using `x`, `y`, `scale`, or the `layout` prop causes layout thrashing.
906
+
907
+ ---
908
+
909
+ ## 🏛️ Tribunal Integration (Anti-Hallucination)
910
+
911
+ **Slash command: `/review` or `/tribunal-full`**
912
+ **Active reviewers: `logic-reviewer` · `security-auditor` · `frontend-reviewer` · `performance-optimizer`**
913
+
914
+ ### ❌ Forbidden AI Tropes
915
+
916
+ 1. **Blind Assumptions:** Never make an assumption without documenting it clearly with `// VERIFY: [reason]`.
917
+ 2. **Silent Degradation:** Catching and suppressing animation errors without logging.
918
+ 3. **Context Amnesia:** Forgetting the user's constraints (e.g., generating `motion.div` in a Server Component).
919
+ 4. **Accessibility Neglect:** Failing to implement `prefers-reduced-motion` support is a hard block.
920
+
921
+ ### ✅ Pre-Flight Self-Audit
922
+
923
+ Review these questions before confirming output:
924
+ ```
925
+ ✅ Did I use "use client" for all components using motion.*?
926
+ ✅ Did I add a unique `key` to every AnimatePresence child?
927
+ ✅ Did I use mode="wait" instead of the removed exitBeforeEnter?
928
+ ✅ Did I use useMotionValue (not useState) for animation values?
929
+ ✅ Did I animate transforms (x, y, scale) instead of layout props?
930
+ ✅ Did I handle prefers-reduced-motion with useReducedMotion()?
931
+ ✅ Did I use LazyMotion + m.div for bundle optimization?
932
+ ✅ Did I stop/cleanup animations on unmount?
933
+ ```
934
+
935
+ ### 🛑 Verification-Before-Completion (VBC) Protocol
936
+
937
+ **CRITICAL:** You must follow a strict "evidence-based closeout" state machine.
938
+ - ❌ **Forbidden:** Declaring a task complete because the output "looks correct."
939
+ - ✅ **Required:** You are explicitly forbidden from finalizing any task without providing **concrete evidence** (terminal output, passing tests, compile success, or equivalent proof) that your output works as intended.