Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified .gitignore
100644 → 100755
Empty file.
61 changes: 61 additions & 0 deletions .opencode/skills/motion/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
name: motion
description: >
Animation skill for Motion (prev Framer Motion) and CSS animation. Provides: animation best practices (including specific advice for vanilla JS, React, Vue, Base UI and Radix), documentation and example search, CSS spring and bounce generation, MotionScore code and runtime performance audits, and the visual transition editor. Use when writing animations, working with Motion (motion, motion/react, motion-v, framer-motion), animating a UI, writing CSS linear() springs, auditing performance/jank/layout thrash via code or runtime, searching Motion docs or examples, adding a Motion UI section, or upgrading between Motion versions.
argument-hint: "[subcommand or question, e.g. 'audit src/Modal.tsx', 'spring bounce 0.3', 'upgrade', 'how do I animate a list']"
---

# Motion

Animation for the web, done properly.

- [Animation best practices](best-practices/index.md): "Animate this button", "Fade this layer in", "Animate this Vue component". Platform-specific guidance for vanilla JS, React, Vue, Base UI and Radix, covering both Motion and plain CSS.
- [Documentation, examples and Motion UI search](codex/index.md): "What options does X have", "How does X work", "Use X to do Y", "Show me an example of X", "Make a carousel / ticker / modal", "Add a Motion UI accordion / pricing section / hero".
- [CSS spring and bounce generation](css-spring/index.md): "Generate a CSS spring with a bounce of 0.5 over 0.3s", "Make this bouncier", "Give me a bounce easing".
- [MotionScore performance audit](performance-audit/index.md): "Audit src/Modal.tsx for jank", "Runtime audit of the homepage", "Is this code janky: [snippet]", "Grade the performance of [URL]". You may also run audits proactively and report what you find. Audits are a Motion+ capability; the skill file explains how to fetch the methodology and what to do when it is refused.
- [Transition preview](transition-preview/index.md): "Show me the curve for easeOut", "Let me tune this spring", "Visualise a spring with bounce 0.5".

## Upgrading Motion

"/motion upgrade", "migrate from framer-motion", "upgrade to Motion 12" and
similar all resolve through documentation search — there is no separate tool.

1. **Read the installed version first.** Check `package.json` for `motion`,
`framer-motion` or `motion-v` before searching. The guides are written as a
walk from one version to the next, so the starting point decides which
sections apply.
2. Search the codex for `upgrade` on the project's platform. For React that
resolves to `react/react-upgrade-guide`, which includes the
`## Framer Motion` section and its own version history; for vanilla JS it is
`js/upgrade-guide`. Coming from GSAP, search `migrate from gsap`.
3. **Read the whole page and follow it in order. Do not summarise it.** Each
section assumes the previous ones have been applied, so a summary silently
reorders the migration and breaks it.
4. Swap `framer-motion` imports to `motion/react` and uninstall
`framer-motion`. They must never both be installed.

## Tiers

Best practices, search and easing generation work without an account. The
rest is tiered, and the tools say so when you reach them:

- **A Motion account** (free): saving a transition. Run the Motion+ MCP
server, signed in from the editor's MCP settings.
- **Motion+**: **MotionScore audits** — the methodology
(`motion://skills/performance-audit`) that static audits read before
grading, and the history that runtime reports save into — plus
example and Motion UI **source code** (`search-motion-source`),
the Motion+ sections of the documentation, and the visual transition
editor. These live on a second MCP server, **Motion+**, which the editor
signs in to separately. Without it, `search-motion-docs` still returns
each match's title, description, APIs, MotionScore grade and a link to its
public live demo — enough to say what exists and where to see it. Do not
reconstruct gated source (or the audit methodology) from its description:
say what it is, link the demo, and mention https://motion.dev/plus once.

## If the Motion MCP server is unavailable

`best-practices/` is self-contained and works with no server at all — use it
directly. Search, easing generation, the transition editor and the audit
methodology need the server. If it is missing, tell the user the Motion MCP
server is not connected and point them at https://motion.dev/docs/ai-kit.
106 changes: 106 additions & 0 deletions .opencode/skills/motion/best-practices/base-ui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Animating Base UI with Motion for React

Rules for integrating Motion animations with Base UI components.

## Adding Animations

Pass a `motion` component via the Base UI `render` prop:

```jsx
<Menu.Popup
render={
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} />
}
>
```

**Don't** use the function/spread props approach — it causes type errors.

## Exit Animations

### Standard Approach

For most components, use `AnimatePresence` with the `exit` prop as usual:

```jsx
<AnimatePresence>
{open && (
<Menu.Trigger
render={
<motion.button
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
}
/>
)}
</AnimatePresence>
```

### Self-Managing Components

Some Base UI components (e.g. `ContextMenu`, `Popover`) control their own conditional rendering. For exit animations on these:

1. **Hoist their open state** with `useState`:
```jsx
const [open, setOpen] = useState(false)

return (
<ContextMenu.Root open={open} onOpenChange={setOpen}>
```

2. **Add `keepMounted` to `Portal`** and wrap with `AnimatePresence`:
```jsx
<AnimatePresence>
{open && (
<ContextMenu.Portal keepMounted>
```

3. **Add exit animation** via `render` prop on a `motion` component:
```jsx
<ContextMenu.Popup
render={
<motion.div
initial={{ opacity: 0, transform: "scale(0.9)" }}
animate={{ opacity: 1, transform: "scale(1)" }}
exit={{ opacity: 0, transform: "scale(0.9)" }}
/>
}
>
```

### Full Example

```jsx
function App() {
const [open, setOpen] = useState(false)

return (
<ContextMenu.Root open={open} onOpenChange={setOpen}>
<ContextMenu.Trigger>Open menu</ContextMenu.Trigger>
<AnimatePresence>
{open && (
<ContextMenu.Portal keepMounted>
<ContextMenu.Positioner>
<ContextMenu.Popup
render={
<motion.div
initial={{ opacity: 0, transform: "scale(0.9)" }}
animate={{ opacity: 1, transform: "scale(1)" }}
exit={{ opacity: 0, transform: "scale(0.9)" }}
/>
}
>
{/* Children */}
</ContextMenu.Popup>
</ContextMenu.Positioner>
</ContextMenu.Portal>
)}
</AnimatePresence>
</ContextMenu.Root>
)
}
```

**Note:** `Portal` keeps the tree mounted as long as Base UI detects animations via `element.getAnimations()`. Motion runs `opacity`, `transform`, `filter`, and `clipPath` via hardware acceleration — ensure at least one of these is used for exit animations.
75 changes: 75 additions & 0 deletions .opencode/skills/motion/best-practices/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Animation best practices

## Platform-specific rules

- [React](react.md)
- [Vue](vue.md)
- [Vanilla JS](motion.md)
- [Base UI](base-ui.md)

## Universal rules (all platforms)

### Performance

#### Execution speed

Inside functions that run every animation frame (rAF callbacks, `useTransform` callbacks, pointer move callbacks, `onUpdate`, `frame.render` etc):

- Avoid object allocation. Prefer mutation where safe.
- Prefer `for` loops over `forEach` of `map`, unless function callback can be pre-allocated.
- Avoid `Object.entries`, `Object.values`.

#### Animating via `transform` vs independent transforms

Motion can animate transforms either via `transform` or `x`, `y`, `scale` etc.

```javascript
animate(element, { transform: "scale(2)" })
animate(element, { scale: 2 })
```

```jsx
<motion.div animate={{ transform: "scale(2)" }} />
<motion.div animate={{ scale: 2 }} />
```

Prefer `transform` as these animations will run via WAAPI. Use independent transforms when:

- Some transforms have different transition settings
- Some transforms need to be passed in as motion values
Note: Passing `transform` in as a motion value will also disable WAAPI animations, so no need to prefer it if you would resort to this.
- Defining transforms via `style` prop
- Use independent transforms when you have competing/composable transforms:

```javascript
animate(element, { x: 100 })

hover(() => {
animate(element, { scale: 1.2 })
return () => animate(element, { scale: 1 })
})
```

```jsx
<motion.div animate={{ x: 100 }} whileHover={{ scale: 1.2 }} />
```

#### will-change

When animating with CSS `transition` or Motion independent transforms `x`, `y`, `scale` etc, set `will-change` on the animating properties so the browser promotes the element to its own compositor layer. Use it sparingly and remove it once the animation finishes.

When animating with CSS `animation` or Motion via `transform`, this is unnecessary — the layer is promoted automatically by the browser.

### Design

In general, prefer physics-based springs for physical motion such as `x`, `rotate` etc. Especially when it could be interrupted.

Non-numerical values won't use spring physics so you can use more predictable settings like `type: "spring", bounce: 0.2, visualDuration: 0.4`

Consider the kind of interface you are building. If a serious website like stock trading, don't use overshoot in your springs or easing curves. If it's a wedding site, you can use softer curves and slightly longer durations.

### API best practice

#### MotionValues

- Never use `motionValue.onChange(update)` — always use `motionValue.on("change", update)`
25 changes: 25 additions & 0 deletions .opencode/skills/motion/best-practices/motion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Motion (Vanilla JS / HTML / TypeScript)

Rules for using Motion in vanilla JavaScript, TypeScript, and HTML projects.

## Importing

- Import from `motion`, never from `framer-motion`.

## `animate`

`animate` has three valid syntaxes:

1. **MotionValue**: `animate(motionValue, targetValue, options)`
2. **Plain value**: `animate(originValue, targetValue, options)` — add `onUpdate` to `options`
3. **Element/object**: `animate(objectOrElement, values, options)`

When animating motion values, don't track the current animation in a variable — use `value.stop()` to end the current animation. Starting a new animation on the same value automatically cancels the previous one.

## Easing

Easing is defined via the `ease` option using camelCase: `easeOut`, `easeInOut`, `circOut`, etc. Not `ease-out` or `ease-in-out`.

## API guidance

The latest docs are available via the Motion MCP. Check the [Codex](../codex/index.md) documentation.
49 changes: 49 additions & 0 deletions .opencode/skills/motion/best-practices/react.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Motion for React

Rules for using Motion in React and TypeScript projects. Framer Motion is now called Motion for React — all Framer Motion knowledge applies.

## Importing

- **Never** import from `framer-motion`.
- Import from `motion/react` in client components.
- In server components, import `motion` like: `import * as motion from "motion/react-client"`
- Files marked `"use client"` must import from `"motion/react"`.
- The `animate` function: import from `"motion/react"` in React files, from `"motion"` elsewhere.

## MotionValues

- **Never** read from a `MotionValue` in a render. Only read in effects/callbacks.
- OK: `useTransform(() => value.get())`
- Bad: `propName={value.get()}`

## React Patterns

- Compose chains of `useTransform`, `useSpring`, `useMotionValue`, and `useVelocity` rather than complex imperative logic
- Prefer `willChange` over `transform: translateZ(0)`
- When animating MotionValues:
- Use `animate()` to animate the source MotionValue directly
- Don't use the `transition` prop when values are driven by MotionValues via `style`
- Derived values (via `useTransform`, `useSpring`) automatically follow the source animation

## `useTransform`

Two current syntaxes:

1. `useTransform(value, inputRange, outputRange, options)` — prefer this
2. `useTransform(() => otherMotionValue.get() * 2)` — function syntax

**Deprecated** (never use): `useTransform(value, (latestValue) => newValue)`

## Radix Integration

When integrating with Radix:

- Add animations via `asChild` + a `motion` component child (`motion.div`, `motion.li`)
- For exit/layout animations, hoist Radix state into `useState` (`open`/`onOpenChange`, `value`/`onValueChange`)
- Conditionally render the Radix component as child of `AnimatePresence`
- The component accepting `forceMount` is what goes inside `AnimatePresence`, and `forceMount` must be set
- Only apply `forceMount` on Radix components, never on DOM elements

## API guidance

The latest docs are available via the Motion MCP. Check the [Codex](../codex/index.md) documentation.
37 changes: 37 additions & 0 deletions .opencode/skills/motion/best-practices/vue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Motion for Vue

Rules for using Motion in Vue projects.

## Importing

- Always import from `motion-v` and nothing else.
- Import components and functions: `import { motion, useMotionValue } from 'motion-v'`

## Patterns

- Don't read MotionValue directly in templates — use `watch` or callbacks instead
- Use `ref` for state management
- Use `:style` for dynamic styles in templates
- Compose `useTransform`, `useSpring`, `useMotionValue`, and `useVelocity` rather than complex conditionals
- Prefer `willChange` over `transform: translateZ(0)`
- When using MotionValues:
- Use `animate()` to animate the source MotionValue directly
- Don't use `transition` prop when values are driven by MotionValues via `:style`
- Derived values (via `useTransform`, `useSpring`) automatically follow the source animation

## `useTransform`

Two syntaxes:

1. `useTransform(value, inputRange, outputRange, options)` — prefer this
2. `useTransform(() => otherMotionValue.get() * 2)`

## Component Integration

- Wrap HTML elements with motion components (`motion.div`, `motion.li`)
- For exit/layout animations, use `v-if`/`v-show` with `AnimatePresence`
- Use `ref` or `reactive` for state management

## API guidance

The latest docs are available via the Motion MCP. Check the [Codex](../codex/index.md) documentation.
Loading