Skip to main content
ClaudeWave
Skill5.3k repo starsupdated 3d ago

figma-implement-motion

Translates Figma motion and animations into production-ready application code. Use when implementing animation/motion from a Figma design — user mentions "implement this motion", "add animation from Figma", "animate this component", provides a Figma URL whose node is animated, or when `get_design_context` returns motion data or instructs you to call `get_motion_context`.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/Devin-AXIS/iPolloWork /tmp/figma-implement-motion && cp -r /tmp/figma-implement-motion/examples/plugin-packages/figma/skills/figma-implement-motion ~/.claude/skills/figma-implement-motion
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Implement Motion

## Overview

This skill guides translation of Figma animations and transitions into runnable code (motion.dev, CSS keyframes, or framework-specific libraries).

Figma exposes motion through two tools:

- `get_motion_context` — authoritative motion tool. Returns the complete animated-node inventory, precomputed code snippets (CSS `@keyframes` + motion.dev), fallback keyframe bindings when snippets are unavailable, and recursive timeline coordination hints (`timelineCohorts`). **Source of truth for animation data and which node IDs animate.**
- `get_design_context` — the design's **structure**: layout, sizing, assets, styling, Code Connect hints, screenshot context, and sometimes **motion placement markers** on animated elements (`data-node-id`, and on split nodes `data-motion-keys` / `data-motion-wrapper-for` / `data-motion-transform-template`). It may render an animated node as a plain element (`div`, `p`, `span`, etc.) or a motion element (`motion.div`); it does not inline the animation values.

**The two are linked by node id, and that's the whole workflow.** `get_motion_context` tells you which nodes animate and gives the keyframe values, easing, timing, and snippets. `get_design_context` tells you what those nodes look like and where they sit. For every node in `get_motion_context.nodes`, find the matching `data-node-id` in design context and merge the motion into that structure — adding or wrapping a `motion.{tag}` when the structural element is plain. When design context has reused a Figma component, the motion node may also include `fallbackNodeId`; use it only as a fallback after trying the exact `nodeId`.

## Skill Boundaries

- Use this skill when the deliverable is motion code in the user's repository.
- If the user asks to create/edit animations inside Figma itself, switch to [figma-use](../figma-use/SKILL.md) and follow that skill instead.
- This skill currently covers **animations** as emitted by `get_motion_context` (snippets plus fallback keyframe tracks, including preset-authored motion resolved into those forms). Broader interactive variant flows may still need product-specific state handling in code.

## Prerequisites

- Figma MCP server connected and accessible.
- Node ID parsed from the Figma URL the user provides. URL format: `https://figma.com/design/:fileKey/:fileName?node-id=1-2` — extract `fileKey` (the segment after `/design/`) and `nodeId` (the value of the `node-id` query parameter, e.g. `42-15`).
- Target codebase. Motion output format adapts to stack (see [Framework Recommendations](#framework-recommendations)).

## Tool Choice

For motion implementation, use both tools with distinct roles:

| Situation | Tool | Why |
|---|---|---|
| Understanding static structure, assets, styles, Code Connect, or visual layout | `get_design_context` | Gives the component/page code reference and asset URLs you need to place animated nodes correctly. |
| Fetching animation data for any node | `get_motion_context` | Purpose-built for motion and the source of truth for timing, easing, snippets, and keyframes. |
| A node has motion markers (`data-motion-keys`, `data-motion-wrapper-for`) | Markers for split *placement*, `get_motion_context` for *values* | Split markers tell you which tracks go on which element; the keyframes/easing/timing and animated-node inventory come from `get_motion_context`. |

`get_motion_context` accepts `recursive: true` (capped at 500 nodes) when you need descendants' motion in one call.

## Required Workflow

### Step 1: Confirm static design context is available

```
get_design_context(fileKey=":fileKey", nodeId="<node-id>")
```

If `get_design_context` has already been called for this node, reuse that output. If not, call it normally now.

Use it as the **structure of record** — hierarchy, sizing, styling, assets, Code Connect hints, screenshot context, and any motion placement markers it happens to include (Step 3). The animated-node inventory and animation values come from `get_motion_context` (Step 2).

### Step 2: Fetch authoritative motion data

```
get_motion_context(fileKey=":fileKey", nodeId="<node-id>", recursive=true)
```

Response shape (one entry per animated node):

- `codeSnippets` — pre-generated CSS `@keyframes` and motion.dev strings. **Use these directly.** Do not regenerate them from fallback track data.
- `keyframeBindings` — bound keyframe tracks, including preset-derived motion resolved into track data, included only as fallback data when both snippet formats are missing.
- `motionSummary` — one-line-per-field natural-language description of the animation. Present **only when there's no snippet** (keyframe-bindings-only motion codegen couldn't express as CSS/motion.dev). Build from it when present; ignore it whenever a snippet exists.
- `fallbackNodeId` — optional fallback id for matching componentized design context. If `nodeId` is an instance-qualified id such as `I4005:6111;30:8005`, D2R may render the reusable component body with the backing component id instead, such as `4002:3957`. In that case, `fallbackNodeId` is the `data-node-id` to look for if exact `nodeId` lookup fails.

Recursive responses also include `timelineCohorts` — a **top-level** array (not per-node) of nodes sharing one timeline: `{ rootNodeId, durationMs, loopMode: 'once' | 'loop' | 'boomerang', memberNodeIds[] }`. For coordinated multi-node motion, drive all `memberNodeIds` from one shared lifecycle using `durationMs` (÷1000 for seconds) and `loopMode` — don't infer timing from sibling order.

Implementation details that matter for LLMs:

- When a snippet exists, `motionSummary`, `timelineDurationMs`, and `transformOrigin` may be omitted to shrink the payload — the snippet already carries duration + transform-origin (motion.dev `duration` / `style={{ transformOrigin }}`, or CSS `animation` / `transform-origin`) and the cohort carries `durationMs`. A missing field never means "no animation."
- Recursive responses dedupe exact duplica