Motion
Agent-era skill suites for Claude Code: engineering lifecycle + distributed systems, gated by the checklist CLI
npx -y skills add IamK77/Skill --skill motionAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
The motion, gesture & feel lens for a frontend build — where every animation earns its place and the feel IS the product. Use after the build works, when adding interaction physics, when a UI feels sluggish / janky or the gesture fights the scroll, or auditing animations. The one shift: every animation maps to a PURPOSE and its dynamics come from physics (spring vs easing, never confused); the feel is the product; compositor- only properties + a real reduced-motion path are non-negotiable — the agent animates because it can, feeling none of the jank, so motion must be gated. Triggers on "animation / transition / motion / spring / damping / bounce / easing / physics", "gesture / drag / scroll / swipe / scroll-vs-drag", "feels laggy / disconnected / jank / stiff / floaty", "prefers-reduced-motion / a11y", "FLIP / View Transitions / scroll-driven", "60fps / compositor / layout thrashing / will-change", "feels native / hand-feel / touch / stagger / orchestration / shared element / morph".
SKILL.md
24.6 KB, as published. Nobody here has run it
motion
!checklist init ${CLAUDE_SKILL_DIR} --force
An atelier is a craftsman's studio — the place where judgment is applied to material, where a decision about how something moves is as considered as a decision about how it looks. motion is the feel lens of the atelier suite: where the other skills decide what is true and what it looks like, motion decides what it does when you touch it — and makes that a system, not a pile of ad-hoc durations and default curves. Its product is a written motion language: every animation named to a purpose, dynamics derived from physics, gesture conflicts resolved by a declared arbitration policy, and a re-tuned reduced-motion path that preserves meaning. It runs across gated stages and will not advance past a GATE until the checklist tool clears it — order enforced, substance yours.
The governing fact: motion without a purpose is noise; noise is the fingerprint of agent-generated work. The agent animates because it can: transition: all 0.3s ease applied to every state change, a spring dropped in for "feel," layout properties animated because they were convenient, and no prefers-reduced-motion path because no one asked for one. Each choice is locally defensible; the sum is jank, wasted attention, and an accessibility failure. The craft is the same subtraction the rest of the suite preaches: name the purpose first; delete what has none; derive the dynamics from physics; make the platform carry the work. Do that and two things happen — the surface communicates (a well-choreographed state transition tells users where they are; a precise spring on release tells them the object has real weight) and it performs (compositor-only properties stay off the main thread; a declared touch-action replaces a non-passive event handler).
This is where the agent era bites:
- The agent animates decoration. It reaches for
transition: all 0.3sor drops a spring on a button fade. Neither has a job; both add noise. The test is merciless: name the purpose or delete the animation. - The agent confuses spring and curve. A hover uses
framer-motion; a modal fade uses a Bézier. Both "work" until the user grabs the modal mid-animation — the Bézier restarts from scratch; the spring would have carried the velocity. Using a curve where physics is needed, or a spring where a simple discrete 200ms ease-out would do, is a category error the agent makes constantly. - The agent forgets gesture arbitration. A draggable panel is placed inside a scrollable list with no
touch-action, nooverscroll-behavior, and no axis-lock — so the finger fights the scroll on every open. The platform primitives exist; they are rarely set. - The agent ships no
prefers-reduced-motionpath. The media query exists, it is never checked, and a user with vestibular sensitivity gets large-translation parallax on every scroll.
Read references/motion-purpose-and-physics.md first — the suite's motion spine; everything from easing direction through spring parameter tiers and orchestration patterns lives here. Load at the start, re-check at every gate.
Speak the user's language. The decisions are the user's — which animations earn their place, how much bounce the brand personality warrants, where the surface archetype (data tool vs marketing) changes the motion budget. Read their fluency and gloss a term on first use (purpose taxonomy, spring / damping ratio ζ, easing curve / cubic-bezier, compositor vs layout properties, FLIP / View Transitions API, scroll-driven animation, overscroll-behavior / touch-action, prefers-reduced-motion, stagger / orchestration). A motion system the user can't explain to a colleague is style imposed, not shared.
The reference library
The depth lives in references/. Open each when a stage sends you there — not all upfront.
- references/motion-purpose-and-physics.md — the heart: the seven-purpose taxonomy (causality / spatial continuity / feedback / progress / attention / depth / expression); the easing-vs-spring decision tree and why they can never be swapped; direction rules (ease-out for entrances, ease-in for exits); duration as a function of distance; spring parameter tiers by element weight; orchestration patterns (shared-element, stagger, container transform, enter/exit coordination, Z-axis lift); the 12 animation principles that UI actually uses; the ethics / attention test; and restraint as the default posture.
- references/gesture-arbitration.md — nested gesture conflict resolution: the core arbitration law (inner scroller owns until boundary); axis lock and activation threshold; mid-gesture handoff without a jump; platform primitives (
touch-action,overscroll-behavior, Pointer Events,setPointerCapture, the passive-listener trap); named conflict patterns and recipes (panel-wraps-scroll, horizontal carousel, swipe-to-delete, pull-to-refresh, edge-swipe drawer, same-axis nested, map/pinch-zoom); and platform convention differences (iOS / Android / Web). - references/the-feel-css-deep-water.md — the CSS mechanics that separate "knows how to animate" from "knows why it works": the rendering pipeline and where each property falls; compositor-only properties and why
will-changeis temporary; FLIP and the View Transitions API; CSS scroll-driven animations (animation-timeline: scroll()/view()); spring numerical integration and why semi-implicit Euler; the stacking-context and containing-block traps that breakfixedoverlays; modern CSS that replaces JS hacks (:has(), container queries, anchor positioning,@property); and how to use DevTools to diagnose frame drops, paint flashing, and cascade failures. - references/reduced-motion-and-performance.md — the non-negotiables as technique: the correct
prefers-reduced-motioncontract (re-tune, don't strip; replace vestibular triggers — translation/scale/parallax/rotation — with cross-fade or instant cut; gestures keep working); flicker ≤ 3×/sec (WCAG 2.3.1); every gesture needs a non-gesture alternative; 60fps / 16.7ms budget and how to hold it; thetransform/opacity-only rule and its exceptions (filter); latency masking and the < 100ms first-frame rule; state machine framing for complex interactions; color animation inoklch/oklab.
The arc is one motion system. Three stages — purpose & physics · gesture & intent · compositor, performance & reduced-motion — turn a working surface into one that feels right: animations earn their place and get the right dynamics; fingers are never fighting the scroll; and a user who prefers reduced motion gets a re-tuned experience, not silence.
Greenfield or audit? Decide the entry. On a new surface, walk STAGE 0→2 in order. On an existing surface, inventory first: every animation (CSS
transition,animation, JS-driven), its duration and property, and its apparent purpose — cluster by whether the purpose is nameable, whether it animates layout properties, and whether a reduced-motion path exists. That inventory is not a deliverable to perfect; it is the raw material the gates consume. The technique for finding where the un-named animations hide (DevTools Animations panel, atransition: allgrep, awill-changeaudit) is in references/the-feel-css-deep-water.md. The taste call stays a gate: for each cluster you still decide which animations earn their place and what the physics should be.
STAGE 0 — Purpose & physics (name every animation to a job; set the physics)
Open references/motion-purpose-and-physics.md. Establish the purpose taxonomy and the motion tokens before anything is tuned.
- Every animation maps to a purpose — or is deleted. Before touching timing or curve, classify into the seven jobs: causality / origin story (show where a thing came from — the button that triggered the panel); spatial continuity (state/route change that preserves the mental map — shared-element morph); feedback (confirm input received, first frame < 100ms); progress / status (loading, determinate vs indeterminate); attention / hierarchy (staggered entrance, what to look at first); depth / spatial model (modal forward, drawer from edge); expression / brand (the emotional layer — spend it on one signature moment). Anything that doesn't map is noise. Delete it. Restraint is the default posture; over-animation is an agent-era tell.
- Physics by role — spring or curve, never confused. The decision is mechanical: discrete, state-to-state, known duration, not interruptible → easing curve (CSS
transition/ Web Animations API). Gesture-driven, interruptible, needs "physics," must carry velocity → spring (stiffness / damping / mass; library-driven). A Bézier curve restarted mid-flight jumps; a spring carries current position and velocity — that round-trip is the fingerprint of native feel. Easing direction: entrance → ease-out (fast start, settle gently — responding to a user action must start instantly); exit → ease-in (accelerate away, get out of the way); on-screen A→B → ease-in-out. Pure ease-in on an entrance is forbidden — slow start reads as stuck. Commit curve tokens (cubic-bezier(0, 0, 0.2, 1)ease-out ·cubic-bezier(0.4, 0, 1, 1)ease-in ·cubic-bezier(0.4, 0, 0.2, 1)ease-in-out · an asymmetric expressive curve for signature moments).linearis reserved for genuinely constant motion: infinite spinner, progress fill, 1:1 drag tracking. - Spring parameters from the damping ratio ζ, not by feel. Design in the two derived quantities: ω₀ = √(k/m) (speed) and ζ = c / (2·√(k·m)) (character). ζ < 1 = underdamped (bounce); ζ = 1 = critical (fastest no-bounce settle); ζ > 1 = overdamped (sluggish, avoid). Design interface:
response(≈ perceived duration,2π/ω₀) andbounce/dampingFraction(≈ ζ). Tiers by element weight: crisp/precise controls ≈ ζ 1.0 / response 0.2–0.3s; standard UI / cards / panels ζ 0.8–1.0 / 0.3–0.5s; modals / large surfaces ζ 0.9–1.0 / 0.4–0.6s; small delight elements ζ 0.5–0.7 / 0.3–0.5s; signature bounce only ζ 0.35–0.5 / 0.4–0.6s. Bounce budget: most of the site ζ ≥ 0.8 (overshoot ≤ ~1.5%); visible bounce is reserved for the few light/signature elements. Overshoot formula:exp(−ζπ/√(1−ζ²)). Commit spring tokens to match the curve tokens. - Intensity calibrated to the surface archetype. The purpose taxonomy is constant everywhere; the motion budget is set by what the surface is for. A marketing/brand surface may spend signature, expressive motion; a data/tool/dashboard surface earns motion only for orientation (state change), causality (what just happened), and latency masking — runs crisp and short, and a re-fire under streaming data must never restart or distract. Decide the budget as a taste gate; the taxonomy enforces it.
- Duration as a function of distance and size. Duration grows sub-linearly with travel and element size. Exit ≈ 0.6–0.8× entrance. Perceptual bands: < ~100ms = not perceived as motion; most functional UI = 150–400ms; > ~400–500ms starts to feel sluggish (signature/hero excepted). First frame of feedback < 100ms even when the full animation is longer. Stagger: 20–60ms between adjacent items, total duration capped (never linearly stagger 50 items — cap visible count or use a decay curve).
GATE — clear before GESTURE
checklist check purpose purpose-and-physics-committedchecklist verify purpose
STAGE 1 — Gesture & intent arbitration (scroll vs drag; conflict resolution)
Open references/gesture-arbitration.md. Every gesture conflict must be declared and resolved before a finger is confused.
- Axis lock and activation threshold. Commit only after displacement > ~8–10px (a tap must not be misread as a drag). Assign by the displacement vector's angle: within ±~30° of an axis belongs to that axis; the middle is a diagonal ambiguity zone — pick one policy (dominant-component-first or a small deadzone) and hold it site-wide. First move locks the axis for the rest of the gesture; the other axis is ignored (kills sideways jitter in a vertical scroll).
- The core arbitration law. The inner scroller consumes vertical gestures; unless it has already hit its boundary in the gesture's direction, at which point the gesture hands off to the outer panel. For a full-height panel containing a list scrolled to top: drag down with
scrollTop=0→ inner can't scroll → hand to panel; drag up → list scrolls, panel still. Once the panel starts dragging, lock the inner scroll until the panel returns to full-height and the gesture reverses. - Mid-gesture handoff — zero-jump is the hard constraint. At handoff, the taker must start from the exact current position and consume only the overflow past the boundary — otherwise the element jumps by the overflow amount. Velocity transfer is a design choice held consistently: chain the residual fling velocity to the outer (iOS-style flick keeps going) or absorb it (stop at the edge, require a deliberate re-pull). Use boundary tolerance
scrollTop ≈ 0 / ≈ maxScroll(~1px). - Platform primitives — declarative first,
preventDefaultlast.overscroll-behavior: containstops scroll-chaining to ancestors (a modal's list at bottom no longer scrollsbody) — first tool to reach for.touch-action: pan-ydeclares browser owns vertical scroll, horizontal gestures go to JS (mandatory for horizontal carousel inside vertical page);pan-xreverse;none= all to JS;manipulation= no double-tap zoom — the declarative workhorse for axis arbitration, and the agent almost never sets it. Pointer Events +setPointerCapture— build for pointer, not mouse-only. Passive-listener trap:touchmoveis passive at document level by default (preventDefaultsilently no-ops); intercepting scroll needs{passive:false}— that hurts scroll perf, so anything solvable withtouch-action/overscroll-behaviormust NOT use non-passivepreventDefault. - Named conflict patterns, declared. Vertical page + horizontal carousel → page
touch-action: pan-y, carousel handles pan-x, angle threshold on diagonal. Swipe-to-delete row in scroll list → angle test, lock once horizontal commits. Pull-to-refresh → fire only atscrollTop=0+ downward + displacement threshold + rubber-band resistance;overscroll-behavior-y: contain. Edge-swipe drawer over horizontal content → reserve first ~16–20px for drawer. Same-axis nested scroll → inner to boundary then chains to outer viaoverscroll-behavior. Map/pinch-zoom inside scroll →touch-action: none, take over everything in JS. - Gesture physics on release. 1:1 during drag — no easing, no spring, direct position tracking. At release: measure velocity over last ~50ms window (EMA of Δx/Δt), never the whole-gesture average (the average badly underestimates a "slow-then-flick"). Project the momentum endpoint:
current + v₀·rate/(1−rate)(v₀ in px/ms, rate ≈ 0.998 normal / 0.99 fast), find nearest snap point to the projection, spring to it carrying v₀. Flick-to-next threshold: |v₀| > ~500 px/s → ignore position, go to next detent in fling direction. Rubber-band: Apple formulab(x,d,c) = (1 − 1/(x·d/c + 1))·cwith d ≈ 0.55, c ≈ container dimension; or simplifieddisplacement × 0.2–0.5. - Interruptibility as a requirement. A spring mid-flight must be grababble: capture current position + velocity, return to 1:1 gesture tracking; on re-release, feed position + velocity back to the spring. This round-trip is the entire secret of "it is one continuous physical object." Model complex draggable interactions as a state machine:
idle → dragging → settling → snapped— explicit legal transitions instead of flag juggling.
GATE — clear before REDUCED-MOTION
checklist check gesture gesture-arbitration-declaredchecklist verify gesture
STAGE 2 — Reduced-motion, compositor-only & 60fps
Open references/reduced-motion-and-performance.md and references/the-feel-css-deep-water.md. These are non-negotiables — not taste decisions.
prefers-reduced-motionis a re-tuned path, not an off switch. Replace the vestibular triggers — large translation, scale, parallax, rotation — with a cross-fade or instant cut; keep the necessary feedback (the user still needs to know a panel opened). For gestures: swap momentum/bounce for a fast fade or instant snap — the gesture still works, only the physics flourish is gone. Flicker ≤ 3×/sec (WCAG 2.3.1). Critical information must never be conveyed by motion alone. Every gesture needs a non-gesture alternative (a swipe-to-dismiss panel also has a close button; keyboard scroll; focusable controls). Persons using switch control or voice control cannot perform gestures — gesture must never be the only path.- Compositor-only properties. The rendering pipeline is parse → style → layout → paint → composite. Animating
width/height/top/left/margintriggers layout reflow and drops frames. Animatetransformandopacityonly;filteris permitted but paint-heavy. To animate a layout change cheaply: FLIP (First/Last measure → Invert with a counter-transform so it looks unmoved → Play the transform to 0) or the View Transitions API (document.startViewTransition()+view-transition-name— automatic FLIP-style + shared-element morph; works for both SPA state changes and MPA cross-document navigation; prefer it over hand-rolled FLIP).will-changeis a temporary hint — set just before the animation, removed after; leaving it on costs GPU memory and may harm more than help. Animate color inoklch/oklabto avoid the sRGB gray dead-zone. - 60fps held under load. 60fps = 16.7ms/frame; 120fps = 8.3ms/frame — the animation must stay on the compositor thread. Don't stack heavy animations on scroll or first-paint. JS-driven heavy animation blocks the main thread and hurts INP. Quantified check: only
transform/opacityanimated · no dropped frames in the Rendering panel · single-interaction main-thread work < ~50ms. For springs: prefer semi-implicit (symplectic) Euler integration over closed-form analyticx(t)— the analytic solution is cheaper but awkward when the target changes mid-flight; per-frame integration handles interruption and target changes natively (why most libraries use it). Use fixed step / sub-stepping at low frame rates to avoid divergence. - Mask latency to preserve the causal story. The user's action must visibly, immediately cause the result; when the real result is slow, show the optimistic/skeleton response inside the < 100ms feedback window so the cause→effect link is never broken.
transform-originis the mechanical implementation of causality: a tooltip scaling from its anchor, a menu expanding from the trigger corner, a modal growing from the button that opened it — set the origin to the causal source, not the element's center. - CSS scroll-driven animations.
animation-timeline: scroll()/view()— no JS, no IntersectionObserver; compositor-friendly when onlytransform/opacityare animated. Prefer over JS-driven scroll effects.
FINAL GATE
checklist check compositor compositor-only-reduced-motion-and-60fpschecklist verify compositorchecklist show— confirm all three stages passed.checklist done— clear this run's state.
The thread through all of it
motion is the suite's kinetic conscience — the place where taste about feel is gated, where the agent's default of "animate everything, animate layout, ignore reduced-motion" is refused. It runs after the surface has its visual system, and its motion language is what lookout later measures as felt performance and what bulwark must keep from regressing. The through-line is the suite's own — push correctness into structure — applied to movement: an animation derived from a purpose taxonomy and a physics system can't drift the way a hand-typed 0.3s ease can. The line that keeps motion honest: the document holds the encodable technique (how ζ controls overshoot, how touch-action routes a gesture axis, how FLIP does layout animation without layout cost); the taste — which animations earn their place, how much bounce the brand warrants, where the surface archetype sets the motion budget — stays a gate the user clears. Blur that line and the skill becomes a recipe book; hold it and it stays a lens.
Anti-patterns (use as a pre-flight checklist)
transition: all 0.3s— never. Name the property, name the duration, name the curve.allanimates layout properties, triggers reflow, hides intent, and fires on properties you didn't mean to animate.- Spring where a curve belongs — hover/fade/discrete open: easing curve. Touch/interruptible/gesture-driven: spring. The test: can the animation be grabbed mid-flight? If yes, spring.
- Curve where a spring belongs — a Bézier restarted mid-animation jumps; a spring carries current position and velocity. Re-triggerable gestures always need a spring.
- Animating layout properties —
width,height,top,left,margin,paddinganimate on the main thread. Use FLIP or View Transitions API instead. will-changeleft on permanently — it promotes the layer, costs GPU memory, and may hurt compositing of nearby elements. Set before the animation, remove after.- No
prefers-reduced-motionpath — the media query is mandatory. Replace vestibular triggers with cross-fade or instant cut; keep necessary feedback; never strip motion entirely and leave the user without state information. - Motion only path — a swipe-to-dismiss must also have a close button; a drag-and-drop must also have keyboard ordering. Gesture as the only path is an accessibility failure.
- Animating color in sRGB/HSL — interpolation crosses the gray dead-zone; use
oklch/oklab. - Purpose-less animation — if you cannot name the purpose from the seven-job taxonomy, delete it. "It looks nice" is not a purpose.
- Uniform stagger on long lists — cap total duration; use a decay curve so later items accelerate rather than linearly staggering all fifty items.
- Whole-gesture average for velocity — the average underestimates a "slow-then-flick." Always EMA over the last ~50ms window.
preventDefaulton passive listeners — silently no-ops at document level; usetouch-actionandoverscroll-behaviorinstead; only drop to{passive:false}when declaration can't cover it, and document why.- No activation threshold on drag — a tap becomes a drag at the first pixel. Require > ~8–10px before committing to a drag decision.
- Mid-gesture handoff with a jump — the incoming owner must start from the exact boundary point and consume only the overflow, not reset position to zero.
- Skipping a GATE — and remember: every animation should trace to a purpose and a physics decision; if it doesn't, it was invented by hand.