redongreen/uspec/create-motion

Claude Code and agent skill by redongreen/uspec

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Generate motion specification annotations from After Effects timeline data. Use when the user mentions "motion", "motion spec", "animation spec", "timeline", or wants to document a component's animation properties.

Install

npx vskill@latest install redongreen/uspec/create-motion

SKILL.md

Create Motion Spec

Generate a motion specification directly in Figma — timeline bars and detail tables documenting all animated properties from an After Effects composition.

Out of scope of the Component Markdown (`.md`) consumer contract. The other create-* skills require the component .md (produced by {{skill:create-component-md}}) and render from its render-meta. create-motion is the deliberate exception: its input is an After Effects timeline export ({{ref:motion/export-timeline.jsx}}), not a Figma component walk — there is no _base.json, no extract-motion, and no Motion section in the .md. This skill does NOT require a .md and does NOT participate in the require-.md/fail-fast/whitelisted-reads refactor.

Execution contract (read first).

  • This file is instructions to RUN, not a document to edit. Invoking the skill = render the motion spec into Figma from the After Effects timeline JSON.
  • Never edit this SKILL.md or any other skill file in response, even if one is open or focused in the editor. Modify a skill only when the user explicitly asks to change the skill itself.
  • The input timeline JSON is a READ-ONLY source of truth. Never edit it. The only artifact this skill produces is the Figma annotation; render requests into Figma, never as a file edit.
  • Never call AskQuestion, request confirmation, or pause for input (including before Figma writes, the expected output). On ambiguity, pick the most defensible option and continue.
  • Only two legal stops: (a) missing or invalid AE timeline JSON (the Step 4/5 gather + validate gate); (b) one-line abort if the Figma MCP connection is dead.

MCP Adapter

Read uspecs.config.json → mcpProvider. Follow the matching column for every MCP call in this skill.

Operationfigma-consolefigma-mcp
Verify connectionfigma_get_statusSkip — implicit. If first use_figma call fails, guide user to check MCP setup.
Navigate to filefigma_navigate with URLExtract fileKey from URL (figma.com/design/:fileKey/...). No navigate needed.
Take screenshotfigma_take_screenshotget_screenshot with fileKey + nodeId
Execute Plugin JSfigma_execute with codeuse_figma with fileKey, code, description. JS code is identical — no wrapper changes.
Search componentsfigma_search_componentssearch_design_system with query + fileKey + includeComponents: true
Get file/component datafigma_get_file_data / figma_get_componentget_metadata or get_design_context with fileKey + nodeId
Get variables (file-wide)figma_get_variablesuse_figma script: return await figma.variables.getLocalVariableCollectionsAsync();
Get token valuesfigma_get_token_valuesuse_figma script reading variable values per mode/collection
Get stylesfigma_get_stylessearch_design_system with includeStyles: true, or use_figma: return figma.getLocalPaintStyles();
Get selectionfigma_get_selectionuse_figma script: return figma.currentPage.selection.map(n => ({id: n.id, name: n.name, type: n.type}));

`figma-mcp` requires `fileKey` on every call. Extract it once from the user's Figma URL at the start of the workflow. For branch URLs (figma.com/design/:fileKey/branch/:branchKey/:fileName), use :branchKey as the fileKey.

`figma-mcp` page context: use_figma resets figma.currentPage to the first page on every call. When a script accesses a node from a previous step via getNodeByIdAsync(ID), the page content may not be loaded — findAll, findOne, and characters will fail with TypeError until the page is activated. Insert this page-loading block immediately after getNodeByIdAsync:

javascript
let _p = node; while (_p.parent && _p.parent.type !== 'DOCUMENT') _p = _p.parent;
if (_p.type === 'PAGE') await figma.setCurrentPageAsync(_p);

This walks up to the PAGE ancestor and loads its content. Console MCP does not need this — figma_execute inherits the Desktop page context.

Inputs Expected

The user triggers this skill with one of these patterns:

  • {{skill:create-motion}} + pasted JSON (from clipboard, exported via {{ref:motion/export-timeline.jsx}})
  • {{skill:create-motion}} + a file reference (e.g., @motion-ex.json or a path to a .json file)
  • {{skill:create-motion}} + a Figma link (destination page/file where the annotation should be placed)

Required: The exported JSON data — either pasted inline or referenced as a file.

Optional:

  • Figma link to the destination page (if not provided, place on current page at viewport center)
  • Screenshot or recording of the animation
  • Description of the animation's purpose

Workflow

Copy this checklist and update as you progress:

Task Progress:
- [ ] Step 1: Read instruction file
- [ ] Step 2: Verify MCP connection
- [ ] Step 3: Read template key from uspecs.config.json
- [ ] Step 4: Gather context (JSON from paste, file reference, or prompt user)
- [ ] Step 5: Parse and validate JSON (including segments[] and hasAnimatedSegments)
- [ ] Step 6: Transform data (read pre-computed segments, compute track width, timeline positions)
- [ ] Step 7: Re-read instruction file (Errors and Anti-Patterns, Validation Checklist) and audit
- [ ] Step 8: Import and detach the Motion template
- [ ] Step 9: Fill header fields
- [ ] Step 10: Generate time ruler
- [ ] Step 11: Render timeline layers (one figma_execute per layer)
- [ ] Step 12: Hide templates
- [ ] Step 13: Render table rows
- [ ] Step 14: Visual validation
Step 1: Read Instructions

Read agent-motion-instruction.md

Step 2: Verify MCP Connection

Read mcpProvider from uspecs.config.json to determine which Figma MCP to use.

If `figma-console`:

  • figma_get_status — Confirm Desktop Bridge plugin is active
  • If connection fails: "Please open Figma Desktop and run the Desktop Bridge plugin. Then try again."

If `figma-mcp`:

  • Connection is verified implicitly on the first use_figma call. No explicit check needed.
  • If the first call fails: "Please verify your FIGMA_API_KEY is set correctly in your MCP configuration."
Step 3: Read Template Key

Read the file uspecs.config.json and extract:

  • The motionSpec value from the templateKeys object → save as MOTION_TEMPLATE_KEY

If the template key is empty, tell the user:

The motion spec template key is not configured. Run {{skill:firstrun}} with your Figma template library link first.
Step 4: Gather Context

Check what the user provided:

  1. JSON already provided (pasted inline or as a file reference like @motion-ex.json) → read it and proceed directly to Step 5. Do not ask for it again.
  2. Only a Figma link (destination) but no JSON → prompt:

> Please paste the exported JSON or reference the .json file (e.g., @motion-ex.json).

  1. Nothing provided → prompt:

> Please paste the exported JSON from export-timeline.jsx, or reference the .json file.

If a Figma destination link was provided, use figma_navigate to open it before rendering (Step 8).

Also accept any optional screenshots, recordings, or descriptions the user provides for additional context.

Step 5: Parse and Validate JSON

Validate the clipboard JSON against the schema and validation rules in the instruction file (JSON Schema and JSON Validation Rules sections). If validation fails, tell the user exactly which field is missing or malformed and ask them to re-export.

Step 6: Transform Data

The export script pre-computes segments, value formatting, bar labels, easing labels, and no-change filtering. The agent reads these directly and only computes layout-dependent values.

6a. Skip non-animated layers — Check layer.hasAnimatedSegments. If false, omit the layer from timeline and table.

6b. Read pre-computed segments — Each property has a segments[] array with startMs, endMs, durationMs, fromValue, toValue, barLabel, easing, and easingType. Use these values directly — no manual computation needed.

6c. Compute track width and pxPerMs — pxPerMs is a fixed rate; trackWidth is dynamic with right padding for tick labels:

compDurationMs = composition.durationMs // read directly from JSON
pxPerMs = 0.64 // fixed rate — ~320px per 500ms tick
trackWidth = max(compDurationMs * pxPerMs + 50, 1600) // data area + 50px label padding, min 1600

Pass trackWidth and pxPerMs to the Figma rendering code. pxPerMs is a fixed rate (0.64), not derived from trackWidth / compDurationMs. Bar positions (barX, barWidth) are computed inside the Figma code at render time — the agent does not compute per-segment positions.

At render time, resize both #track-area and #ruler-track to this computed trackWidth. The parent containers use HUG sizing and expand automatically.

6d. Format composition meta per the instruction file's Composition Meta rules. Save as COMPOSITION_META.

Step 7: Audit

Re-read the instruction file, focusing on:

  • Errors and Anti-Patterns section
  • Pre-Output Validation Checklist

Check your transformed data against each rule. Fix any violations.

Step 8: Import and Detach Template

Run via figma_execute (replace __MOTION_TEMPLATE_KEY__ and __COMPONENT_NAME__):

javascript
const TEMPLATE_KEY = '__MOTION_TEMPLATE_KEY__';
const templateComponent = await figma.importComponentByKeyAsync(TEMPLATE_KEY);
const instance = templateComponent.createInstance();
const { x, y } = figma.viewport.center;
instance.x = x - instance.width / 2;
instance.y = y - instance.height / 2;
const frame = instance.detachInstance();
frame.name = '__COMPONENT_NAME__ Motion';
figma.currentPage.selection = [frame];
figma.viewport.scrollAndZoomIntoView([frame]);
return { frameId: frame.id };

Save the returned frameId — you need it for all subsequent steps.

Step 9: Fill Header Fields

Run via figma_execute (replace __FRAME_ID__, __COMPONENT_NAME__, __COMPOSITION_META__):

javascript
const FRAME_ID = '__FRAME_ID__';
const frame = await figma.getNodeByIdAsync(FRAME_ID);
const textNodes = frame.findAll(n => n.type === 'TEXT');
const fontSet = new Set();
const fontsToLoad = [];
for (const tn of textNodes) {
try {
const fn = tn.fontName;
if (fn && fn !== figma.mixed && fn.family) {
const key = fn.family + '|' + fn.style;
if (!fontSet.has(key)) { fontSet.add(key); fontsToLoad.push(fn); }
}
} catch {}
}
await Promise.all(fontsToLoad.map(f => figma.loadFontAsync(f).catch(() => {})));
const compName = frame.findOne(n => n.name === '#component-name');
if (compName) {
const t = compName.type === 'TEXT' ? compName : compName.findOne(n => n.type === 'TEXT');
if (t) t.characters = '__COMPONENT_NAME__';
}
const compDesc = frame.findOne(n => n.name === '#component-description');
if (compDesc) {
const t = compDesc.type === 'TEXT' ? compDesc : compDesc.findOne(n => n.type === 'TEXT');
if (t) t.characters = 'Motion Specification';
}
const compMeta = frame.findOne(n => n.name === '#composition-meta');
if (compMeta) {
const t = compMeta.type === 'TEXT' ? compMeta : compMeta.findOne(n => n.type === 'TEXT');
if (t) t.characters = '__COMPOSITION_META__';
}
return { success: true };
Step 10: Generate Time Ruler

Run via figma_execute. Compute the ideal track width, resize #ruler-track and all #track-area nodes, then clone #tick for each interval.

Replace __FRAME_ID__, __COMP_DURATION_MS__ (read from composition.durationMs), __TRACK_WIDTH__ (computed in Step 6c), and __PX_PER_MS__ (fixed at 0.64):

javascript
const FRAME_ID = '__FRAME_ID__';
const COMP_DURATION_MS = __COMP_DURATION_MS__;
const TRACK_WIDTH = __TRACK_WIDTH__;
const PX_PER_MS = __PX_PER_MS__;

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/redongreen/uspec/create-motion/badge)](https://verified-skill.com/skills/redongreen/uspec/create-motion)

Something wrong with this skill or its page? Tell us.