Design & UX No. 364

UI Motion skill for Claude Code

Fast, purposeful motion with real numbers. Claude gives each animation a job and a budget: 100 to 150 milliseconds for feedback, up to 350 for modals, exits faster than entrances, ease-out on the way in and ease-in on the way out. It animates only transform and opacity, prefers interruptible transitions, @starting-style and View Transitions over heavy libraries, and adds a reduced-motion version that keeps feedback but drops travel.

  • nameui-motion
  • size188 lines · ≈2,150 tokens
  • files1
  • licenseMIT
  • updated

What Claude sees before loading it

This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.

description: Designs purposeful interface motion with concrete numbers — duration by distance and size, easing per direction, exit-faster-than-enter, stagger limits, interruptible transitions, transform/opacity-only performance, View Transitions, @starting-style entry, springs, and prefers-reduced-motion alternatives. Use when adding micro-interactions, animating modals, menus, toasts, lists, or page changes, when motion feels slow, floaty, or janky, or when reviewing animation for accessibility.

Try asking

  • Add tasteful animations to our modal, dropdown and toast components
  • Everything on this page fades in slowly and it feels sluggish. Fix the motion
  • Make our animations respect prefers-reduced-motion without losing feedback

Use it when

  • Adding micro-interactions or component transitions
  • Motion feels slow, floaty, bouncy, or janky
  • Animating page changes or list reorders
  • Reviewing animations for accessibility

Skip it when

  • You need GSAP timelines or scroll-driven storytelling implementation, use web-animations instead
  • The jank comes from heavy React re-renders rather than animation, use react-rerenders instead

How to check the result

A 4x CPU-throttled performance trace shows no long frames, rapid toggling reverses smoothly from the current position, and reduced-motion emulation removes all travel.

The common mistake it prevents

Writing transition: all 0.3s ease everywhere, which animates unintended properties like theme colors and uses the wrong easing for both entering and exiting.

~/.claude/skills/ui-motion/SKILL.md

--- name: ui-motion description: Designs purposeful interface motion with concrete numbers — duration by distance and size, easing per direction, exit-faster-than-enter,… license: MIT ---

Motion is done when every animation has a job (feedback, orientation, continuity, or attention), runs within the duration budget for its size, uses easing that matches its direction, can be interrupted mid-flight, stays at 60fps on a throttled CPU, and has a reduced-motion alternative. If removing an animation loses no information, remove it.

Ground rules

  • Motion explains change. It shows where something came from, where it went, or that an action registered. Decoration-only motion is a cost.
  • Fast by default. Most UI motion lives between 120 and 300ms; anything over 500ms in product UI feels like waiting.
  • Animate transform and opacity only. Layout properties (width, height, top, margin) trigger layout every frame.
  • Exits are faster than entrances (about 70–80% of the enter duration): users have already decided to move on.
  • State-change animations must be interruptible: use CSS transitions or spring/physics libraries that retarget, not fixed keyframe sequences.

Avoid the generic look

Default reflexBetter
Every section fades up 40px on scrollOne reveal per section at most, 8–16px travel, or none for content-heavy pages
hover: scale(1.05) on every cardSubtle elevation or border change; scale only for media thumbnails
Bouncy springs on everythingCritically damped (no bounce) for UI; small bounce (0.1–0.25) only for playful, direct-manipulation moments
600–1000ms durations150–300ms; the larger the element and the farther it moves, the longer
transition: all 0.3s easeName properties; pick an easing for the direction
Spinner for every waitSkeletons for content, inline progress for actions; no indicator under ~300ms

Duration budget

What movesDuration (enter)Notes
Color/opacity change, hover, press100–150msPress feedback ≤ 100ms
Small elements: tooltip, checkbox, toggle, chip150–200msTooltip show delay 300–500ms, hide immediately
Medium: dropdown, popover, toast200–250msExit 150ms
Large: modal, drawer, bottom sheet250–350msExit 200ms; backdrop fades with it
Page / route transition, shared element300–450msKeep under 500ms
Stagger between list items20–50ms eachCap total stagger at ~300ms (animate first 6–8 items, rest instantly)

On mobile, durations can be ~20% shorter for small elements; on large screens, large movements may need ~20% longer to avoid feeling abrupt.

Easing

DirectionEasingCSS
Entering (appear, expand)Decelerate (ease-out)cubic-bezier(0.16, 1, 0.3, 1) (strong) or cubic-bezier(0.2, 0, 0, 1)
Exiting (dismiss, collapse)Accelerate (ease-in)cubic-bezier(0.4, 0, 1, 1)
Moving on screen (A → B)Standard (ease-in-out)cubic-bezier(0.4, 0, 0.2, 1)
Continuous (progress, spinners)Linearlinear
Direct manipulation release (drag, swipe)SpringLibrary spring, velocity-aware

Store these as tokens (--ease-enter, --ease-exit, --ease-move, --duration-fast: 150ms, --duration-base: 250ms, --duration-slow: 350ms).

Workflow

- [ ] 1. List state changes in the feature; mark which need motion and why
- [ ] 2. Assign duration and easing from the tables
- [ ] 3. Choose the mechanism (CSS transition, @starting-style, View Transition, library)
- [ ] 4. Implement with transform/opacity; make it interruptible
- [ ] 5. Add reduced-motion alternative
- [ ] 6. Verify timing, jank, and interruption

3. Choose the mechanism

NeedMechanism
Hover, focus, toggle, state classesCSS transition on named properties
Element entering the DOM or leaving display:noneCSS @starting-style + transition-behavior: allow-discrete
Dialog / popover open-closeNative <dialog> / popover + @starting-style and ::backdrop
Route change or list reorder in one documentView Transitions API document.startViewTransition()
Multi-page site navigationCross-document view transitions @view-transition { navigation: auto; }
Gestures, springs, layout animation in ReactMotion (motion/react)
Complex timelines, scroll storytellingGSAP or CSS scroll-driven animations (see web-animations)

Entry animation without JS:

.toast {
  transition: opacity 200ms var(--ease-enter), transform 200ms var(--ease-enter),
              display 200ms allow-discrete, overlay 200ms allow-discrete;
  opacity: 1; transform: translateY(0);
}
@starting-style { .toast { opacity: 0; transform: translateY(8px); } }
.toast[hidden] { display: none; opacity: 0; transform: translateY(4px);
  transition-duration: 150ms; transition-timing-function: var(--ease-exit); }

View transition for a state update:

function update(mutateDom) {
  if (!document.startViewTransition) return mutateDom();
  document.startViewTransition(mutateDom);
}
::view-transition-old(root), ::view-transition-new(root) { animation-duration: 250ms; }
.product-image { view-transition-name: product-hero; }   /* unique per page */

Motion (React) spring that settles in a predictable time:

import { motion } from "motion/react";
<motion.div layout transition={{ type: "spring", visualDuration: 0.3, bounce: 0.15 }} />

5. Reduced motion

Reduce, do not delete: keep feedback, remove travel, parallax, zoom, and autoplay.

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important; animation-iteration-count: 1 !important;
    transition-duration: 1ms !important; scroll-behavior: auto !important;
  }
  .toast { transition: opacity 150ms linear; }   /* keep a gentle fade where it helps */
}
import { useReducedMotion } from "motion/react";
const reduce = useReducedMotion();
const variants = { hidden: { opacity: 0, y: reduce ? 0 : 12 }, shown: { opacity: 1, y: 0 } };

Accessibility requirements:

  • Anything that moves, blinks, or scrolls automatically for more than 5 seconds needs a pause/stop control (WCAG 2.2.2).
  • Nothing flashes more than three times per second (WCAG 2.3.1).
  • Motion triggered by interaction can be disabled (WCAG 2.3.3, AAA; honoring prefers-reduced-motion covers it in practice).

Micro-interaction recipes

InteractionSpec
Button presstransform: scale(0.97) on :active, 100ms ease-out; no scale on hover
Toggle switchThumb translateX, 150ms standard easing; color crossfade same timing
Dropdownopacity 0→1 + translateY(-4px → 0) + scale(0.98 → 1), origin at trigger, 180ms enter / 120ms exit
ModalBackdrop fade 200ms; panel opacity + translateY(8px) or scale(0.96 → 1) 250ms enter / 180ms exit
DrawertranslateX(100% → 0) 300ms enter / 220ms exit
ToastSlide 8–16px from its edge + fade, 200ms; auto-dismiss ≥ 5s, pause on hover/focus
AccordionAnimate a grid row grid-template-rows: 0fr → 1fr (or interpolate-size: allow-keywords where supported) 200–250ms
Success checkStroke draw 250–300ms after the action resolves; not on every save
Skeleton shimmer1.2–1.6s linear loop, low contrast; respect reduced motion (static)

Gotchas

  • transition: all animates properties you did not intend (including color on theme switch) and costs performance.
  • Animating height: auto directly does not work in most browsers; use the grid-rows trick, FLIP, or a library layout animation.
  • will-change on many elements wastes GPU memory; add it just before an animation and remove it after, or skip it.
  • Keyframe animations restart from the beginning when re-triggered; toggling quickly produces jumps. Use transitions for reversible states.
  • Animating on page load delays LCP-visible content: never hide the hero behind an entrance animation.
  • Each view-transition-name must be unique on the page at transition time.

Verification

  • DevTools Performance with 4× CPU throttle: no long frames (> 16.7ms) during the animation; no Layout/Recalculate Style per frame for transform-only animations.
  • DevTools Animations panel: inspect and slow down to 10% to check easing and choreography.
  • Interrupt test: toggle every animated state rapidly; it reverses smoothly from the current position.
  • Emulate prefers-reduced-motion: reduce (DevTools Rendering): no travel, parallax, or autoplay; feedback still visible.
  • Inventory: every animation in the feature has a stated purpose in the PR description.

About this skill

Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.

esc