uxskill
Star on GitHub
Language
English 简体中文 繁體中文 日本語 한국어 हिन्दी Bahasa Indonesia Tiếng Việt ไทย العربية Español Français Deutsch Português Русский Türkçe Italiano
Blog · Reference · 2026-05-28

# 57 motion presets for AI coding: Framer Motion, GSAP, and CSS.

Most AI coding tools have one motion default: fade-in-up, 600ms, on everything. That's the slop. ux-skill's motion manifest ships 57 named presets, each available as Framer Motion config, GSAP call, and raw CSS, with explicit `prefers-reduced-motion` branches and performance notes. Same intent, three engines, queryable as data. Here's the tour.

By the ux-skill team · 9 min read · MIT-licensed open source

## The shape of one entry

Pulled from `data/motion-presets.json`. Every preset has an `id`, a `name`, a category, an engine-agnostic `tokens` block (duration, easing, transform), and three engine-specific snippets ready to paste. Plus a reduced-motion branch and a performance note.

    // data/motion-presets.json: entries[0]
    {
      "id": "fade-up-12px",
      "name": "Fade-Up 12px",
      "category": "Entry",
      "tokens": {
        "duration_ms": 360,
        "easing": "cubic-bezier(0.16, 1, 0.3, 1)",
        "transform_from": "translateY(12px)",
        "transform_to": "translateY(0)",
        "opacity_from": 0,
        "opacity_to": 1
      },
      "stacks": {
        "framer_motion": "{ initial: { y: 12, opacity: 0 }, animate: { y: 0, opacity: 1 }, transition: { duration: 0.36, ease: [0.16, 1, 0.3, 1] } }",
        "gsap": "gsap.from(el, { y: 12, opacity: 0, duration: 0.36, ease: 'expo.out' })",
        "css": "@keyframes fade-up-12 { from { transform: translateY(12px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } .fade-up-12 { animation: fade-up-12 360ms cubic-bezier(0.16, 1, 0.3, 1) both; }"
      },
      "reduced_motion": "instant: fade only, no transform",
      "performance": "transform + opacity only: GPU composited"
    }

The `tokens` block is engine-agnostic and durable: when someone wants to add a fourth engine (Motion One, Anime.js, JS Web Animations API) the work is generating new snippets from those same tokens, not redoing the intent.

## The 57 presets at a glance

Eight categories, 57 entries. The breakdown matches the way a real product surface needs motion, not just entry animations.

Category | Count | Representative presets
--- | --- | ---
Entry | 10 | `fade-up-12px`, `fade-in-pure`, `scale-in-soft`, `slide-up-card`, `blur-in-12`
Hover | 8 | `lift-2px`, `tint-shift`, `border-warm`, `icon-rotate-12`, `card-tilt-2deg`
Transition | 8 | `route-crossfade`, `tab-slide`, `drawer-push`, `view-trans-card`
Scroll | 7 | `scroll-pin-hold`, `parallax-depth-3`, `reveal-stagger`, `scrub-fold`
State | 7 | `state-flip`, `loading-skeleton`, `success-tick`, `error-shake`
Exit | 6 | `fade-out-fast`, `shrink-out`, `slide-down-exit`
Attention | 6 | `pulse-soft`, `glow-cycle`, `row-highlight`, `shake-error`
Press | 5 | `press-scale-97`, `press-darken`, `haptic-bounce`
Total | 57 | Every preset in every category has all three engine snippets.

## One preset, three engines: fade-up-12px

Here's the most-used Entry preset in all three forms, with the reduced-motion branch:

### Fade-Up 12px

fade-up-12px · Entry
360ms expo.out easing transform + opacity

#### Framer Motion

    import { motion } from 'framer-motion';

    <motion.div
      initial={{ y: 12, opacity: 0 }}
      animate={{ y: 0, opacity: 1 }}
      transition={{ duration: 0.36, ease: [0.16, 1, 0.3, 1] }}
    >
      <Card />
    </motion.div>

#### GSAP

    import { gsap } from 'gsap';

    gsap.from(el, {
      y: 12,
      opacity: 0,
      duration: 0.36,
      ease: 'expo.out'
    });

#### CSS

    @keyframes fade-up-12 {
      from { transform: translateY(12px); opacity: 0; }
      to   { transform: translateY(0);    opacity: 1; }
    }

    .fade-up-12 {
      animation: fade-up-12 360ms cubic-bezier(0.16, 1, 0.3, 1) both;
    }

    /* Reduced motion: fade only, no transform */
    @media (prefers-reduced-motion: reduce) {
      .fade-up-12 {
        animation: fade-in-200ms linear both;
      }
    }

## prefers-reduced-motion is not optional

Every preset declares a `reduced_motion` contract. Three categories of behavior, depending on what the motion is doing:

- "no change": the preset is already opacity-only or covers a state transition that the user expects. Keep as is.
- "instant: fade only, no transform": keep the opacity arc, drop the transform. The semantics survive; the velocity doesn't.
- "omit: render final state immediately": for decorative attention-grabbers like `pulse-soft` and `glow-cycle`, the motion is dropped entirely.

The linter has a rule (`fade-in-up-no-reduced-motion`) that fires on any animation using `translate` without a `@media (prefers-reduced-motion: reduce)` branch nearby. The linter's 51 rules includes four motion-category rules; this is the one that catches the AI default most often.

## When to pick each engine

#### Pick CSS when

The motion is declarative, doesn't need to be controlled by state, and the duration is under 1.2 seconds. CSS keyframes are the cheapest to ship (no JS, no library, no runtime cost) and the most performant: the browser runs them on the compositor thread. Use CSS for Entry animations, Hover lifts, Press states, basic Attention, and most Exit animations.

Bytes shipped: zero. Composite cost: minimal. Control surface: minimal.

#### Pick Framer Motion when

The animation is bound to React state: a route change, a tab switch, a drawer open, a layout shift. Framer Motion's `AnimatePresence` and `layoutId` patterns are unmatched for component-tree animations. Also pick it when you need gestures (drag, hover with imperative control) or layout animations (FLIP).

Bytes shipped: ~35KB gzipped (tree-shakeable). Composite cost: low. Control surface: deep React integration.

#### Pick GSAP when

The animation is a timeline: multiple elements with overlapping cues, scroll-driven scrubbing, complex easing, or staggers. GSAP's ScrollTrigger is the canonical tool for scroll-pinned cinema (the `scroll-pin-hold` preset). It's also the right pick when the motion needs to run outside React (vanilla JS, Svelte, Astro islands).

Bytes shipped: ~40KB gzipped core, +15KB ScrollTrigger. Composite cost: moderate (runs on rAF). Control surface: timeline-first.

### A rule of thumb that holds up

For most product surfaces, the right split is: CSS for 70% of the motion (entry, hover, press, simple transitions), Framer Motion for 20% (route, drawer, layout), GSAP for 10% (scroll cinema, complex timelines). If your codebase is 90% Framer Motion, you're probably paying React-tree-traversal costs for animations CSS would do for free.

## Performance: only animate cheap properties

Every preset's `performance` field documents which properties get animated. The shortlist of cheap ones, properties the browser can composite without layout or paint:

- `transform`: translate, scale, rotate, skew. Free.
- `opacity`: free.
- `filter`: moderate cost; `blur` is heavy.

The shortlist of expensive ones, anything that triggers layout:

- `top`, `left`, `width`, `height`, `margin`, `padding`: all expensive. The linter (`animate-layout-property`) catches these.
- `box-shadow`: moderate; consider `filter: drop-shadow` on the parent.

Every preset in the manifest is built from cheap properties. If you need an expensive animation, you build it yourself and document why.

## Querying motion from your agent

The presets are exposed over MCP via the `ux_motion_presets` tool. The agent can pull a single preset, the full catalog, or a filtered subset:

    // Filter by category, then take the first match
    const presets = await mcp.call("ux_motion_presets", {
      category: "Scroll"
    });

    // Returns: scroll-pin-hold, parallax-depth-3, reveal-stagger, scrub-fold, ...

    // Or pull one by id
    const preset = await mcp.call("ux_motion_presets", {
      id: "scroll-pin-hold"
    });

Full MCP server walkthrough covers the wiring for Claude Desktop, Cursor, and Windsurf.

## The scroll-pin-hold story

Two of the 57 presets do real cinema work, and they're worth a dedicated section because the AI default is to scroll the page like a Word document and the cinema language is what actually feels modern in 2026.

`scroll-pin-hold` pins the section to the viewport for the duration of its scroll budget, runs an inner timeline scrubbed to scroll position, and releases on exit. `parallax-depth-3` defines three depth layers (background 0.4x, mid 1x, foreground 1.6x of scroll velocity) so the section reads as a 3D camera move.

Both are GSAP + ScrollTrigger presets. CSS scroll-driven animations are landing in browsers but the spec is still moving, and Framer Motion's `useScroll` doesn't pin natively. Until the platform stabilizes, GSAP is the cleanest path.

The new dark editorial cinema homepage uses both presets on every major section.

> AI coding tools ship one motion default: fade-in-up on everything. The fix is not "less motion." It's the right motion per role.

Honesty card

#### 57 presets is not "every animation ever."

For bespoke product moments (a custom data viz, a branded loader, a one-off marketing micro-interaction) you'll still write motion yourself. The manifest covers the 90% case where the motion is recognizable across the industry, and the goal is to use the right one instead of inventing a worse one. The remaining 10% is where the brand earns its identity, and that's the right place to spend effort.

The Framer Motion snippets target v11+. The GSAP snippets target v3+. The CSS snippets are evergreen.

## How motion plugs into the rest of the engine

The recommender doesn't ship motion in isolation. When you run `/ux-recommend` against a brief, the response includes a curated motion subset (typically 4 to 7 presets) picked to match the style and tone. A "calm editorial" recommendation pulls `fade-up-12px`, `scroll-pin-hold`, and `lift-2px`. An "operational density" recommendation pulls `state-flip`, `row-highlight`, and `success-tick`. The presets are the same; the curation is the design call.

More on how the recommender composes a system, including which manifests it pulls from.

## Related reading

- The 51 AI design fingerprints, including the motion fingerprints.
- Best Claude Code design skills: which ones ship motion libraries.
- Dark editorial cinema: scroll-pin-hold in production.
- Regex linter: including the four motion rules.
- MCP: calling motion presets from any host.
- Compare: ui-ux-pro-max doesn't ship a motion library.

Install the engine

## One library. Three engines. 57 presets.

Same manifest powers the CLI, the MCP server, and the recommender: every preset available as Framer Motion, GSAP, and CSS. Reduced-motion handling included. MIT, no telemetry, no account.

$ /plugin marketplace add Laith0003/ux-skill
$ /plugin install ux@ux-skill
or
$ pip install uxskill
or
$ npx uxskill@alpha init

## Related reads

- Dark editorial cinema design
- Dogfooding ux-skill
- 35 AI design fingerprints
- Anti-AI-slop tools for Claude Code