eliasoulkadi/shokunin/motion-craft

Claude Code and agent skill by eliasoulkadi/shokunin

Trust tier 2
  • community
LLM review pending
Source check pending

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

Design GPU-accelerated animations with Web Animations API (WAAPI), Scroll-Driven Animations (ScrollTimeline/ViewTimeline), FLIP technique, easing systems, and accessibility (prefers-reduced-motion). Use when user asks to create animations, transitions, scroll effects, page transitions, or interactive motion for web UIs. Do NOT use for canvas-based animations (Three.js, PixiJS), video editing, or non-web (native mobile) motion design.

Install

npx vskill@latest install eliasoulkadi/shokunin/motion-craft

SKILL.md

Motion Craft

Animations that communicate, not decorate. Every frame must earn its place.

Inspired by Emil Kowalski (Linear, Sonner, Vaul), Paul Lewis (Google Chrome), and the CSS Animation Working Group.

Workflow

The Animation Decision Framework

Before writing any animation code, answer these four questions in order.

1. Should this animate at all?
FrequencyDecisionReason
100+ times/day (keyboard shortcuts, command palette, search toggle)No animation. Ever.Animation makes frequent actions feel delayed. Raycast has no open/close animation. Optimal.
Tens of times/day (hover effects, list navigation)Remove or drastically reduceUsers notice animation latency on repeated interactions
Occasional (modals, drawers, toasts)Standard animationThe right place for motion
Rare / first-time (onboarding, confirmation, celebration)Add delightThese moments earn longer, richer animations

Never animate keyboard-initiated actions. These repeat hundreds of times daily.

2. What is the purpose?

Every animation must have a clear answer to "why does this animate?"

PurposeExampleValid?
Spatial consistencyToast enters and exits from the same direction?
State indicationButton morphs to show "sent"?
FeedbackButton scales down on press, confirming the interface heard the user?
Preventing jarring changesElements fading in instead of appearing abruptly?
ExplanationMarketing animation showing how a feature works?
"It looks cool" AND the user sees it oftenGratuitous motion?
3. What easing should it use?
ScenarioEasingCSS
Element enteringease-out (starts fast, feels responsive)cubic-bezier(0, 0, 0.2, 1)
Element exitingease-in (starts slow, grabs attention briefly then accelerates away)cubic-bezier(0.4, 0, 1, 1)
On-screen movement / morphingease-in-outcubic-bezier(0.4, 0, 0.2, 1)
Hover / color changeeasecubic-bezier(0.25, 0.1, 0.25, 1)
Constant motion (marquee, progress bar)linearcubic-bezier(0, 0, 1, 1)
UI interactions (buttons, tooltips, dropdowns)strong ease-outcubic-bezier(0.23, 1, 0.32, 1)

Critical: never use ease-in for UI entering animations. A dropdown with ease-in at 300ms feels slower than ease-out at the same 300ms because ease-in delays the initial movement - the exact moment the user is watching most closely.

4. How fast should it be?
ElementDurationEasing
Button press feedback (scale)100-160mscubic-bezier(0.23, 1, 0.32, 1)
Tooltip enter / exit125-200mscubic-bezier(0.23, 1, 0.32, 1)
Dropdown / select / popover150-250mscubic-bezier(0.23, 1, 0.32, 1)
Hover transform (scale, lift)200mscubic-bezier(0.23, 1, 0.32, 1)
Modal / dialog enter200-300mscubic-bezier(0, 0, 0.2, 1)
Modal / dialog exit150-200mscubic-bezier(0.4, 0, 1, 1)
Drawer / sheet enter200-400mscubic-bezier(0.32, 0.72, 0, 1) (iOS-like)
Toast enter300-400mscubic-bezier(0.23, 1, 0.32, 1)
Toast exit200-300mscubic-bezier(0.4, 0, 1, 1)
Page transition300-400mscubic-bezier(0.4, 0, 0.2, 1)
Scroll reveal (fade + slide up)400-800mscubic-bezier(0.16, 1, 0.3, 1)
Loading shimmer1000-1500ms (infinite)linear
Hold-to-delete (press)2000mslinear (deliberate)
Hold-to-delete (release)200mscubic-bezier(0.23, 1, 0.32, 1) (snappy)
Marketing / explanatory800-3000msvariable, depends on narrative

Rule: UI animations should stay under 300ms. A 180ms dropdown feels noticeably more responsive than a 400ms one.

Custom easing curves

The built-in CSS ease / ease-in / ease-out are too weak. Use these stronger custom variants:

css
:root {
--ease-out-strong: cubic-bezier(0.23, 1, 0.32, 1); /* UI interactions, Emil's signature */
--ease-in-out-strong: cubic-bezier(0.77, 0, 0.175, 1); /* On-screen movement */
--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); /* iOS-like drawer */
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); /* Slow reveals, dramatic */
--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); /* Alternative strong ease-out */
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); /* Slight overshoot, sparingly */
}

Sources: easing.dev, easings.co


The 60fps Golden Rule

16.67ms per frame. Verify with Chrome DevTools Performance tab. Only animate transform and opacity.

PropertyGPU composite?Triggers
transform (translate, scale, rotate)? YesComposite only
opacity? YesComposite only
filter?? SometimesPaint + Composite
clip-path?? SometimesPaint + Composite
width, height, top, left, margin, padding, border-width? NoLayout + Paint + Composite
box-shadow? NoPaint + Composite
color, background-color? NoPaint only

Review Format (Required)

When reviewing UI animation code, you MUST use a markdown table with Before | After | Why columns. Do NOT use a list with "Before:" and "After:" on separate lines.

Correct format:
BeforeAfterWhy
transition: all 300mstransition: transform 200ms ease-outSpecify exact properties; avoid all which triggers layout recalculation
transform: scale(0)transform: scale(0.95); opacity: 0Nothing in the real world appears from nothing
ease-in on dropdowncubic-bezier(0.23, 1, 0.32, 1)ease-in starts slow - feels sluggish. Strong ease-out gives instant feedback
No :active state on buttontransform: scale(0.97) on :activeButtons must feel responsive to press
transform-origin: center on popovertransform-origin: var(--radix-popover-content-transform-origin)Popovers should scale from their trigger. Modals stay centered.
transition: 300ms without prefers-reduced-motion@media (prefers-reduced-motion: reduce) { transition-duration: 0.01ms }WCAG 2.1 requires reduced-motion support
animate={{ x: 100 }} in Framer Motionanimate={{ transform: "translateX(100px)" }}Framer Motion x/y are NOT hardware-accelerated. Use transform string for GPU.

Wrong format (never do this):

Before: transition: all 300ms
After: transition: transform 200ms ease-out

CSS Transitions (preferred for UI)

CSS transitions are interruptible - they can be retargeted mid-animation. Keyframes restart from zero. For any interaction that can be triggered rapidly (toasts, toggles, dropdowns), transitions produce smoother results.

css
.button {
transition: transform 160ms ease-out;
}
.button:active {
transform: scale(0.97);
}
.tooltip {
transition: transform 125ms ease-out, opacity 125ms ease-out;
transform-origin: var(--transform-origin);
}
Asymmetric enter / exit timing

Enter can be slower (user is deciding), exit must be fast (system responding):

css
.overlay {
transition: clip-path 200ms ease-out; /* release: fast */
}
.button:active .overlay {
transition: clip-path 2s linear; /* press: slow and deliberate */
}
Tooltips: skip delay on subsequent hovers
css
.tooltip {
transition: transform 125ms ease-out, opacity 125ms ease-out;
transform-origin: var(--transform-origin);
}
.tooltip[data-instant] {
transition-duration: 0ms;
}
Enter states with @starting-style (Chrome-only as of 2026, not yet Baseline)
css
.toast {
opacity: 1;
transform: translateY(0);
transition: opacity 400ms ease-out, transform 400ms ease-out;
@starting-style {
opacity: 0;
transform: translateY(100%);
}
}
Note: @starting-style and scroll-driven animations (animation-timeline: view(), animation-timeline: scroll()) are Chrome-only as of 2026. Use JavaScript fallbacks for cross-browser support.
Hold-to-delete pattern
css
.overlay {
clip-path: inset(0 100% 0 0);
transition: clip-path 200ms ease-out; /* release: fast */
}
.button:active .overlay {
clip-path: inset(0 0 0 0);
transition: clip-path 2s linear; /* press: slow and deliberate */
}

BUTTONS - Tactile Feedback

Every pressable element needs physical response.

css
.button {
transition: transform 160ms cubic-bezier(0.23, 1, 0.32, 1);
}
.button:active {
transform: scale(0.97);
}

The scale must be subtle (0.95-0.98). Applied to all pressable elements - buttons, cards, list items, tabs.


POPOVERS - Origin-Aware

Popovers scale from their trigger, not center. Modals stay centered.

css
/* Radix UI */
.popover {
transform-origin: var(--radix-popover-content-transform-origin);
}
/* Base UI */
.popover {
transform-origin: var(--transform-origin);
}
/* Modals (always centered) */
.modal {
transform-origin: center;
}

Whether the user notices individually does not matter. In the aggregate, unseen details compound.


Stagger Animations

When multiple elements enter together, stagger their appearance.

css
.item {
opacity: 0;
transform: translateY(8px);
animation: fadeIn 300ms ease-out forwards;
}
.item:nth-child(1) { animation-delay: 0ms; }
.item:nth-child(2) { animation-delay: 50ms; }
.item:nth-child(3) { animation-delay: 100ms; }
.item:nth-child(4) { animation-delay: 150ms; }
@keyframes fadeIn {
to {
opacity: 1;
transform: translateY(0);
}
}

Keep stagger delays short (30-80ms between items). Never block interaction while stagger plays.


CSS Animations (keyframes)

Use for predetermined, non-dynamic animations. Keyframes restart from zero on interruption - avoid for UI.

css
@keyframes enter-right {
from { opacity: 0; transform: translateX(16px); }
to { opacity: 1; transform: translateX(0); }
}
.enter {
animation: enter-right 300ms ease-out forwards;
}

Spring Animations

Springs feel more natural than duration-based animations because they simulate real physics. They maintain velocity when interrupted.

When to use springs
  • Drag interactions with momentum
  • Elements that feel "alive" (Apple Dynamic Island style)
  • Gestures that can be interrupted mid-animation
  • Decorative mouse-tracking interactions
Apple approach (recommended - easier to reason about)
js
{ type: "spring", duration: 0.5, bounce: 0.2 }
Traditional physics (more control)
js
{ type: "spring", stiffness: 100, damping: 10, mass: 1 }

Keep bounce subtle (0.1-0.3). Avoid bounce in most UI. Use for drag-to-dismiss and playful interactions only.

Spring-based mouse interactions
jsx
import { useSpring } from 'framer-motion'
const springX = useSpring(mouseX * 0.1, { stiffness: 100, damping: 10 })

Without spring: feels artificial, instant. With spring: feels natural, has momentum.


WEB ANIMATIONS API (WAAPI)

Programmatic CSS animations. Hardware-accelerated, interruptible, no library needed.

javascript
const element = document.querySelector('.card')
const animation = element.animate([
{ transform: 'scale(1)', opacity: 1 },
{ transform: 'scale(0.95)', opacity: 0.7 },
], {
duration: 200,
easing: 'cubic-bezier(0.23, 1, 0.32, 1)',
fill: 'both',
})
animation.pause()
animation.play()
animation.reverse()
animation.finish()
animation.finished.then(() => console.log('done'))

WAAPI beats Framer Motion under heavy load - it runs off the main thread. Use for dynamic, interruptible animations where CSS keyframes fall short.


SCROLL-DRIVEN ANIMATIONS

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

Repository stars
112
Repository forks
14
7-day trend score
24

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 27 Aug 2026, 2 months ago.

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/eliasoulkadi/shokunin/motion-craft/badge)](https://verified-skill.com/skills/eliasoulkadi/shokunin/motion-craft)

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