diff --git a/skills/motion_ui/skill.md b/skills/motion_ui/skill.md
index 7fdf9e41..e7fe30b6 100644
--- a/skills/motion_ui/skill.md
+++ b/skills/motion_ui/skill.md
@@ -1,3 +1,4 @@
+````md
---
name: motion-ui
description: "Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns."
@@ -11,66 +12,64 @@ Focused on **performance, accessibility, and usability** — not decoration.
**by Jatan**
+---
+
## When to Use
Use this motion system when motion:
-* Guides attention (e.g., onboarding, key actions)
-* Communicates state (loading, success, error, transitions)
-* Preserves spatial continuity (layout changes, navigation)
+- Guides attention (onboarding, primary actions)
+- Communicates state (loading, success, error, transitions)
+- Preserves spatial continuity (navigation, layout changes)
### Appropriate Scenarios
-* Interactive components (buttons, modals, menus)
-* State transitions (loading → loaded, open → closed)
-* Navigation and layout continuity (shared elements, crossfade)
+- Interactive UI (buttons, modals, menus)
+- State transitions (open/close, loading states)
+- Navigation transitions and shared elements
### Considerations
-* **Accessibility**: Always support reduced motion
-* **Device adaptation**: Adjust for low-end devices
-* **Performance trade-offs**: Prefer responsiveness over visual smoothness
+- Accessibility must be preserved (reduced motion support)
+- Low-end device performance must be respected
+- Prefer responsiveness over visual smoothness
-### Avoid Using Motion When
+### Avoid Motion When
-* It is purely decorative
-* It reduces usability or clarity
-* It impacts performance negatively
+- It is purely decorative
+- It reduces clarity or usability
+- It impacts performance
---
-## How It Works
-
-### Core Principle
+## Core Principle
Motion must:
-* Guide attention
-* Communicate state
-* Preserve spatial continuity
+- Guide attention
+- Communicate state
+- Preserve spatial continuity
If it does none → remove it.
---
-### Installation
+## Installation
```bash
npm install motion
-```
+````
---
-### Version
+## Versions
* `motion/react` → default
-* `framer-motion` → legacy
-
-Do not mix.
+* `framer-motion` → legacy (do not mix)
---
-### Motion Tokens
+## Motion Tokens
```ts
export const motionTokens = {
@@ -93,23 +92,25 @@ export const motionTokens = {
---
-### Performance Rules
+## Performance Rules
-**Safe**
+### Safe Properties
* transform
* opacity
-**Avoid**
+### Avoid
-* width / height
-* top / left
+* width
+* height
+* top
+* left
Rule: responsiveness > smoothness
---
-### Device Adaptation
+## Device Adaptation
```ts
const isLowEnd =
@@ -121,22 +122,26 @@ const duration = isLowEnd ? 0.2 : 0.4
---
-### Accessibility
+## Accessibility
-#### JS (useReducedMotion)
+### Reduced Motion (React)
```tsx
import { motion, useReducedMotion } from "motion/react"
const reduce = useReducedMotion()
-
+export function Example() {
+ return (
+
+ )
+}
```
-#### CSS
+### CSS
```css
@media (prefers-reduced-motion: reduce) {
@@ -150,7 +155,7 @@ const reduce = useReducedMotion()
}
```
-#### Tailwind
+### Tailwind
```html
@@ -158,74 +163,79 @@ const reduce = useReducedMotion()
---
-### Architecture & Patterns
+## Core Patterns
-#### Core Patterns
-
-* Hover → `whileHover`
-* Tap → `whileTap`
-* In view → `whileInView`
-* Scroll linked → `useScroll`
-* Conditional → `AnimatePresence`
-* Layout small → `layout`
-* Layout large → avoid
-* Complex → `useAnimate`
-
-#### Layout & Transitions
-
-* Shared transitions → `layoutId`
-* Presence transitions → `AnimatePresence`
+* hover → whileHover
+* tap → whileTap
+* in-view → whileInView
+* scroll → useScroll
+* conditional → AnimatePresence
+* small layout → layout
+* large layout → avoid
+* complex → useAnimate
---
-### Advanced Patterns (Concepts)
+## Layout System
-* Parallax (scroll-linked transforms)
-* Scroll storytelling (sticky sections)
-* 3D tilt (pointer-based transforms)
-* Crossfade (shared layoutId)
-* Progressive reveal (clip-path)
-* Skeleton loading (looped opacity)
-* Micro-interactions (hover/tap feedback)
-* Spring system (physics-based motion)
+* layoutId → shared transitions
+* AnimatePresence → mount/unmount transitions
---
-### Modal Essentials
+## Advanced Patterns
-* Focus trap
-* Escape close
-* Scroll lock
-* ARIA roles
+* Parallax scrolling
+* Scroll storytelling sections
+* 3D pointer tilt
+* Crossfade transitions
+* Clip-path reveals
+* Skeleton loading loops
+* Micro-interactions
+* Spring physics motion
-#### Minimal Example
+---
+
+## Modal System (Production Safe)
```tsx
-import { useEffect, useRef } from "react"
+import { useEffect, useRef, useState } from "react"
import { motion, AnimatePresence } from "motion/react"
-// Placeholder hooks (implement or replace with libraries)
+type ModalProps = {
+ open: boolean
+ onClose: () => void
+}
+
function useFocusTrap(ref: React.RefObject, active: boolean) {
useEffect(() => {
if (!active || !ref.current) return
+
const el = ref.current
+
const focusable = el.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
)
+
const first = focusable[0]
const last = focusable[focusable.length - 1]
- function handleKey(e: KeyboardEvent) {
+ if (first) first.focus()
+
+ const handleKey = (e: KeyboardEvent) => {
if (e.key !== "Tab") return
+ if (!first || !last) return
+
if (e.shiftKey && document.activeElement === first) {
- e.preventDefault(); last?.focus()
+ e.preventDefault()
+ last.focus()
} else if (!e.shiftKey && document.activeElement === last) {
- e.preventDefault(); first?.focus()
+ e.preventDefault()
+ first.focus()
}
}
el.addEventListener("keydown", handleKey)
- first?.focus()
return () => el.removeEventListener("keydown", handleKey)
}, [active, ref])
}
@@ -233,25 +243,31 @@ function useFocusTrap(ref: React.RefObject, active: boolean) {
function useScrollLock(active: boolean) {
useEffect(() => {
if (!active) return
+
const prev = document.body.style.overflow
document.body.style.overflow = "hidden"
- return () => { document.body.style.overflow = prev }
+
+ return () => {
+ document.body.style.overflow = prev
+ }
}, [active])
}
-export function Modal({ open, closeModal }: { open: boolean; closeModal: () => void }) {
+export function Modal({ open, onClose }: ModalProps) {
const ref = useRef(null)
useFocusTrap(ref, open)
useScrollLock(open)
useEffect(() => {
- function onKey(e: KeyboardEvent) {
- if (e.key === "Escape") closeModal()
+ const onKeyDown = (e: KeyboardEvent) => {
+ if (e.key === "Escape") onClose()
}
- if (open) window.addEventListener("keydown", onKey)
- return () => window.removeEventListener("keydown", onKey)
- }, [open, closeModal])
+
+ if (open) window.addEventListener("keydown", onKeyDown)
+
+ return () => window.removeEventListener("keydown", onKeyDown)
+ }, [open, onClose])
return (
@@ -259,6 +275,7 @@ export function Modal({ open, closeModal }: { open: boolean; closeModal: () => v
v
exit={{ scale: 0.95, opacity: 0 }}
className="bg-white p-6 rounded"
>
-
+
)}
)
}
+```
-// Usage
-export function Example() {
- const [open, setOpen] = React.useState(false)
- const openModal = () => setOpen(true)
- const closeModal = () => setOpen(false)
+---
- return (
- <>
-
-
- >
- )
+## Scroll Parallax
+
+```tsx
+import { useScroll, useTransform, motion } from "motion/react"
+
+export function Parallax() {
+ const { scrollYProgress } = useScroll()
+ const y = useTransform(scrollYProgress, [0, 1], [0, -80])
+
+ return
}
```
---
-### SSR Safety
-
-* Match initial states
-* Avoid implicit animation origins
-
----
-
-### Debugging
-
-Check:
-
-* Wrong import
-* Missing `"use client"`
-* Missing `key`
-* Hydration mismatch
-* Layout misuse
-* State-driven animation
-
----
-
-### QA
-
-* No CLS
-* Keyboard works
-* Focus trapped
-* ARIA correct
-* Reduced motion works
-* No hydration warnings
-* Animations stop on unmount
-
----
-
-### Anti-Patterns
-
-* Animating layout properties
-* Infinite animations without purpose
-* Over-staggering lists
-* Ignoring reduced motion
-* Using motion for decoration
-
----
-
-### Philosophy
-
-Motion is interaction design.
-
----
-
-### Final Rule
-
-> If motion does not improve UX → remove it.
-
----
-
-## Examples
-
-### Button Interaction
+## Skeleton Loading
```tsx
import { motion } from "motion/react"
-export function Button() {
- return (
-
- Click me
-
- )
-}
-```
-
----
-
-### Reduced Motion Example
-
-```tsx
-import { motion, useReducedMotion } from "motion/react"
-
-export function FadeIn() {
- const reduce = useReducedMotion()
-
+export function Skeleton() {
return (
)
}
@@ -390,7 +332,19 @@ export function FadeIn() {
---
-### Stagger List
+## Shared Layout
+
+```tsx
+import { motion } from "motion/react"
+
+export function Shared() {
+ return
+}
+```
+
+---
+
+## Stagger List
```tsx
import { motion } from "motion/react"
@@ -410,8 +364,10 @@ const item = {
export function List() {
return (
- {[1,2,3].map(i => (
- Item {i}
+ {[1, 2, 3].map(i => (
+
+ Item {i}
+
))}
)
@@ -420,68 +376,37 @@ export function List() {
---
-### Modal with AnimatePresence
+## Debug Checklist
-```tsx
-import { motion, AnimatePresence } from "motion/react"
-
-export function Modal({ open }) {
- return (
-
- {open && (
-
- )}
-
- )
-}
-```
+* correct import (`motion/react`)
+* `"use client"` in Next.js
+* no missing keys
+* no layout shift (CLS)
+* no hydration mismatch
+* reduced motion works
+* keyboard navigation works
---
-### Scroll Parallax
+## Anti-Patterns
-```tsx
-import { useScroll, useTransform, motion } from "motion/react"
-
-export function Parallax() {
- const { scrollYProgress } = useScroll()
- const y = useTransform(scrollYProgress, [0, 1], [0, -80])
-
- return
-}
-```
+* animating layout (width/height)
+* decorative motion
+* infinite motion without purpose
+* ignoring reduced motion
+* over-staggering lists
---
-### Skeleton Loading
+## Philosophy
-```tsx
-import { motion } from "motion/react"
-
-export function Skeleton() {
- return (
-
- )
-}
-```
+Motion is interaction design.
---
-### Shared Layout (Crossfade)
+## Final Rule
-```tsx
-import { motion } from "motion/react"
+> If motion does not improve UX → remove it.
-export function Shared() {
- return
-}
```
-
+```