../

App animation & motion

Motion design for apps and websites: what motion is for, Disney's principles adapted to interfaces, easing curves and springs, durations, choreography, shared-element and view transitions, loading, gestures, performance and reduced motion, plus the implementation vocabulary (CSS, the Web Animations API, View Transitions, Motion, GSAP, Lottie, Rive, iOS and Android springs) you need to ask for motion specs and code. It applies the general method in prompting fundamentals. The code side is in CSS animation and Web Animations API; screens and states are in UI/UX; motion graphics for video are in video editing.

How experts think

Novice asks forExpert thinks about
"make it animate nicely"what the motion explains: where something came from, what changed, that an action worked
a long, smooth animationshort: most interface motion takes 100–500 ms, and repeated actions should feel instant
one easing for everythingentering things decelerate, leaving things accelerate, on-screen moves ease in and out
a separate animation per screena motion system: duration, easing and spring tokens shared by the whole app
everything moving at oncechoreography: one focal movement, related things moving together, staggers in reading order
an animation that plays to the endinterruptible motion that responds when the user acts mid-animation
a fancy effectperformance: animating only cheap properties, holding the frame rate on a low-end phone
motion for everyonea reduced-motion version for people who get dizzy or nauseous from movement
"just build it"a spec engineers can implement: property, from, to, duration, delay, easing, trigger

Two consequences for prompting. First, describe the state change and its purpose, not the animation: "the card expands into the detail view so users keep their place" lets the model choose a container transform; "make it slide" does not. Second, ask for numbers: durations in milliseconds, cubic-bézier values or spring parameters, and a reduced-motion fallback. Words like "snappy" and "smooth" mean different things to everyone.

Vocabulary

TermWhat it meansSay it like
Micro-interactiona small, single-purpose animated response: a toggle, a like, a press"A micro-interaction for the favorite button: press, fill, settle"
Transitionthe animated change between two states or screens"Spec the transition from list to detail"
Enter / exitan element appearing / disappearing"Enter from the bottom, exit faster than it entered"
Easinghow speed changes over an animation's duration"Name the easing for each movement"
Ease-out (decelerate)starts fast, slows to a stop: feels responsive"Entering elements decelerate"
Ease-in (accelerate)starts slow, speeds up: things leaving for good"Dismissed toasts accelerate off-screen"
Ease-in-out (standard)slow, fast, slow: moving between two on-screen positions"Reordering uses standard easing"
Linearconstant speed; looks mechanical for movement"Linear only for spinners and progress"
Cubic béziera curve defined by four numbers, cubic-bezier(x1, y1, x2, y2); y outside 0–1 overshoots"Give the exact cubic-bezier values"
Keyframea defined value at a point in time; the system fills in between"Keyframes at 0%, 60% and 100%"
Interpolation (tweening)computing the in-between frames"Interpolate color in OKLCH, not RGB"
Duration / delayhow long it runs / how long before it starts"250 ms duration, no delay on the lead element"
Springmotion simulated as a mass on a spring instead of a fixed curve and duration"Use a spring so drags can hand off velocity"
Stiffnesshow strongly the spring pulls toward its target; higher is snappier"Higher stiffness, same damping"
Damping / damping ratioresistance that stops oscillation / damping relative to the amount that just prevents overshoot"Damping ratio 0.8: a hint of overshoot, no wobble"
Massthe simulated weight; higher feels heavier and slower"Keep mass at 1; tune stiffness and damping"
Overshoot / bouncepassing the target and settling back"No overshoot on opacity or color"
Velocityspeed carried into an animation, usually from a gesture"Start the settle animation with the fling's velocity"
Interruptible / retargetingcan change direction mid-flight from its current position and speed"If the user taps again mid-close, it reverses from where it is"
Staggeroffsetting the start of several similar animations"Stagger list items 30 ms apart, max 8 items"
Choreographythe order and relationship of several movements in one transition"Choreograph: container first, then content fades in"
Shared-element (hero) transitionone element visibly travels from the old screen into the new one"The thumbnail becomes the header image"
Container transforma Material pattern: a container (card, button) grows into the next screen"Use a container transform from the card to the detail page"
Shared axisa Material pattern: screens move together along x, y or z to show a spatial relationship"Shared x-axis for steps in the wizard"
Fade througha Material pattern: the old screen fades out, then the new one fades and scales in, for unrelated screens"Fade through between bottom-nav tabs"
Crossfadethe old and new fade simultaneously"Crossfade the images; don't slide them"
FLIPFirst, Last, Invert, Play: animate a layout change with transforms"Animate the reorder with FLIP, not by animating top/left"
View transitiona browser API that snapshots the old and new page states and animates between them"Use the View Transitions API for route changes"
Skeleton screengray placeholder shapes shaped like the content that is loading"Skeleton with a subtle shimmer; static under reduced motion"
Determinate / indeterminate progressshows how much is done / shows only that work is happening"Determinate progress for uploads"
Optimistic UIshowing the result before the server confirms, rolling back on failure"Optimistic like with a rollback animation"
Direct manipulationthe element tracks the finger or pointer exactly"The sheet follows the finger 1:1 while dragging"
Rubber bandingresistance and spring-back when dragging past a boundary"Rubber-band past the top, then spring back"
Snap pointspositions a dragged element settles to"Sheet snap points at 30%, 60% and 100% height"
Parallaxlayers moving at different speeds as you scroll"No parallax: it's a vestibular trigger"
Scroll-driven animationanimation progress tied to scroll position rather than time"Reading progress bar driven by scroll"
Frame rate / frame budgetframes per second / time available per frame (16.7 ms at 60 Hz, 8.3 ms at 120 Hz)"Must hold 60 fps on a mid-range Android phone"
Jankvisible stutter from dropped frames"Find what causes jank in this animation"
Compositorthe stage of rendering that combines layers; transform and opacity can animate there cheaply"Only animate properties the compositor can handle"
Reduced motionan OS setting asking apps to minimize movement"Specify the reduced-motion version of every animation"
Vestibular disorderan inner-ear balance condition; screen motion can cause dizziness and nausea"Treat large slides and zooms as vestibular risks"
Motion tokensnamed, shared values for durations, easings and springs"Use duration-medium-2 and easing-standard-decelerate tokens"
Motion speca table of what animates, from what to what, how long and with which easing"Write a motion spec table I can hand to engineers"
Storyboard / animatickey frames with timings, drawn or described / a rough timed preview"Storyboard the transition frame by frame with timestamps"

Purpose of motion

PurposeWhat it explainsExampleIf it's missing
Feedback"your action registered"a button compresses on press; a checkmark draws inusers tap twice
Orientationwhere things live in spacea drawer slides from the edge it lives onusers don't know how to get back
Continuitythis is the same object, changeda thumbnail grows into the full imagecontext is lost on every screen change
Hierarchy and attentionwhat to look at firstthe new item highlights; items enter in reading orderthe change goes unnoticed
Statusthe system is working, and how far alongskeletons, spinners, progress barsthe app looks frozen
Delight and brandpersonalitya celebration on the first completed taskfine, as long as it's rare and quick

If an animation serves none of these, cut it or make it near-instant.

Principles

Disney animators Frank Thomas and Ollie Johnston set out the 12 principles of animation in The Illusion of Life: Disney Animation (1981). Most translate to interfaces in a quieter form.

PrincipleIn filmIn UISay it like
Squash and stretchshapes deform to show weight and speeda slight scale on press (about 0.97), not rubbery"Press scales to 0.97 and springs back"
Anticipationa small move before the main actiona hover or press state before the result; a slight dip before a sheet rises"Add a hover state that hints the card is clickable"
Stagingpresent one idea clearlydim the background behind a dialog; one thing moves at a time"Scrim fades in under the dialog"
Straight ahead and pose to posedrawing frame by frame vs key poses firstkeyframes vs physics; most UI is pose to pose"Define start and end poses; let the spring do the rest"
Follow-through and overlapping actionparts keep moving after the body stopscontent settles a beat after its container; a small overshoot"Content fades in 50 ms after the container lands"
Slow in and slow outacceleration and decelerationeasing; never linear for movement"Decelerate on enter"
Arcsnatural motion follows curvesa floating action button travels in a slight arc as it expands"Move along an arc, not a straight diagonal"
Secondary actiona smaller action supports the main onean icon rotates as its menu opens"Chevron rotates 180° as the panel expands"
Timingthe number of frames sets weight and mooddurations scale with size and distance"Small 150 ms, panels 300 ms, screens 400 ms"
Exaggerationpush beyond realism for claritykeep it small; error shakes are the exception"A 3-cycle horizontal shake on wrong password"
Solid drawingvolume and perspectiveconsistent light, depth and elevation; shadows grow as things lift"Shadow deepens as the card lifts"
Appealcharacters people want to watchcoherent tokens make motion feel like one product"Same easing family everywhere"

Easing and springs

Easing curves

Material Design 3's easing tokens (values as published in Google's Jetpack Compose Material 3 library) are a good default set:

Token (Material 3)cubic-bezierUse for
Standard(0.2, 0, 0, 1)moves that start and end on screen
Standard decelerate(0, 0, 0, 1)elements entering
Standard accelerate(0.3, 0, 1, 1)elements leaving
Emphasized decelerate(0.05, 0.7, 0.1, 1)expressive, larger entrances
Emphasized accelerate(0.3, 0, 0.8, 0.15)expressive exits
Legacy (Material 2) standard(0.4, 0, 0.2, 1)still common in older systems
Linearlinearspinners, progress, color loops, scroll-linked motion

CSS's built-in keywords (ease, ease-in, ease-out, ease-in-out) are milder versions of the same shapes. A cubic bézier's four numbers are the two control points of a curve from (0, 0) to (1, 1); a y value above 1 makes the motion overshoot. The curves are plotted in CSS animation.

curve: time →, progress ↑ the same move, 2 s each end linear constant speed spinners, progress ease-in accelerate: leaving (0.4, 0, 1, 1) ease-out decelerate: entering (0, 0, 0.2, 1) ease-in-out standard: on-screen (0.4, 0, 0.2, 1) back overshoot, settle (0.34, 1.56, 0.64, 1) past

Same distance, same duration, different feel. These are the Material 2-era values; Material 3's are in the table above.

Springs

A spring has no fixed duration: it runs until it settles, can start with a gesture's velocity, and retargets smoothly when interrupted. That is why native platforms use springs for most motion.

ParameterWhat it doesRaising it
Stiffness (tension)the pull toward the targetfaster, snappier
Damping (friction)resistance to movementless bounce, slower settle if very high
Masssimulated weightslower and heavier
Damping ratiodamping ÷ critical damping: below 1 overshoots (underdamped), 1 is the fastest settle without overshoot (critically damped), above 1 creeps in (overdamped)less bounce
Initial velocityspeed at the start, usually from a flinga livelier start
Perceptual duration and bouncea designer-friendly way to set a spring: how long it feels and how bouncy (0 = none)—

Material 3's newer spring tokens (as published in the Jetpack Compose Material 3 library) show what "standard" and "expressive" mean in numbers. Spatial springs move position, size, rotation and shape; effects springs change color and opacity and never overshoot.

SchemeSpringDamping ratioStiffness
Standardfast / default / slow spatial0.91400 / 700 / 300
Expressivefast spatial0.6800
Expressivedefault / slow spatial0.8380 / 200
Bothfast / default / slow effects1.03800 / 1600 / 800

To move a spring between libraries, match the damping ratio and stiffness (with mass 1), then check it by eye: libraries differ in units and defaults.

Durations

MotionTypical durationSource and notes
Press, toggle, checkbox, color change≈ 50–150 msNN/g suggests about 100 ms for simple feedback; Material 3 short1–short3
Small change: tooltip, chip, icon swap≈ 150–200 msMaterial 3 short3–short4
Menu, dropdown, popover≈ 150–250 msexit faster than entry
Dialog, sheet, expanding card≈ 250–400 msNN/g: 200–300 ms for substantial changes; Material 3 medium1–medium4
Full-screen or route transition≈ 300–500 msNN/g puts about 400 ms as the upper limit for large moves on big screens; Material 3 long1–long2
Expressive: onboarding, celebration≈ 500–1000 ms, rarelyMaterial 3 extra-long tokens
Stagger step≈ 20–50 ms per itemcap the whole sequence at about 300 ms
Enter vs exitexits shorterMaterial's older guidance: about 225 ms in, 195 ms out on mobile; NN/g's example is 300 ms in, 200–250 ms out
Device sizelarger screens, longer movesMaterial's older guidance: tablets about 30% longer than phones, wearables about 30% shorter
Material 3 duration tokens1234
short50100150200
medium250300350400
long450500550600
extra-long7008009001000

Values in milliseconds. NN/g's main warning: animations are far more often too long than too short.

motion most interface motion: 100–500 ms press, toggle, color 50–150 tooltip, chip, icon 150–200 menu, popover 150–250 dialog, sheet, card 250–400 screen, route 300–500 expressive moments 500–1000, rarely stagger step 20–50 per item, ≤ 300 total 0 200 400 600 800 1000 ms Material 3 tokens short medium long extra-long
Bigger changes get more time, but almost everything fits between 100 and 500 ms.

Choreography and transitions

TechniqueRuleSay it like
Lead elementone thing moves first and most; others follow"The card leads; text and buttons follow 50 ms later"
Staggerin reading order, small steps, capped"Stagger 30 ms, max 6 items, then the rest appear together"
Same directionrelated elements move the same way"Everything in the sheet moves up together"
Spatial logicforward moves one way, back the other"Push left on forward navigation, right on back"
Enter and exitenter decelerating from where it lives; exit accelerating, faster, toward where it goes"Toast enters from the bottom in 250 ms, exits down in 200 ms"
Container transforma container grows into the next view"Card to detail: container transform, 400 ms, emphasized"
Shared axisscreens slide or scale together on x, y or z"Wizard steps: shared x-axis"
Fade throughunrelated screens: out, then in with a slight scale"Tabs: fade through, 300 ms"
Shared elementone element visibly travels between screens"Avatar moves from list row to profile header"
1 1 1 2 2 3 3 4 5 5 6 7 8 the list, slowed about 17× start 30 ms apart, 200 ms each 7+ start with 6 0 100 200 300 ms all in at 350 ms dashed: space reserved

Stagger in reading order, cap it after a few items, and reserve the space first.

Shared-element APIs by platform: on the web, the View Transitions API (view-transition-name; same-document transitions are Baseline 2025, cross-document ones are in Chromium and Safari) and Motion's layoutId; in SwiftUI, matchedGeometryEffect, and from iOS 18 the zoom navigation transition; in Jetpack Compose, SharedTransitionLayout with Modifier.sharedElement.

0 ms · tap a card shared: its image and title exist on both screens same name on each pair, e.g. view-transition-name: card-42 or Motion's layoutId focus moves now, not at the end ≈ 150 ms · mid-flight container grows (transform) image scales into the header title glides to its new spot other cards fade out easing: emphasized decelerate ≈ 400 ms · landed image is the header, title sits under it: same objects body and button fade in after the image lands users keep their place back · shorter than the way in shrinks into its slot in the grid other cards fade back in tap mid-flight: reverses from where it is (interruptible) reduced motion: 150 ms crossfade shared element, about 400 ms back 0 200 400 ms

1/4

0 ms: the user taps a card. Its image and title exist on both screens, so each pair gets the same name (a view-transition-name, or Motion's layoutId). Focus moves now, not when the motion ends.

Loading, gestures and physics

Loading

WaitShowSource
under about 1 snothing; a spinner would only flickerNN/g
about 2–10 s, one modulea spinner in that moduleNN/g
full-page load under about 10 sa skeleton screen shaped like the contentNN/g
over about 10 sa determinate progress bar with a time estimateNN/g
likely-success actionsoptimistic UI with a rollback animation on failure—

Skeleton shimmer is itself motion: keep it slow and subtle, and make it static under reduced motion.

Gestures

BehaviorRule
Direct manipulationduring a drag the element tracks the finger exactly; no easing
Velocity handoffon release, a spring starts with the finger's speed, so a fling carries on naturally
Thresholdsrelease past a distance or speed threshold commits the action; otherwise it springs back
Snap pointssettle to defined positions, chosen by position plus velocity
Rubber bandingincreasing resistance past a boundary, then spring back
Interruptiblea new touch catches the element mid-animation instead of waiting
Alternativesevery drag needs a tap or button alternative (WCAG 2.5.7)

Performance

RuleWhy
Animate transform (translate, scale, rotate) and opacitybrowsers can run them on the compositor, often on the GPU, without layout or paint
Avoid animating width, height, top, left, marginthey trigger layout every frame; use FLIP or a transform
Be careful with filter, backdrop-filter, large blurs and shadowsexpensive per pixel; fade a pre-rendered shadow layer instead
Respect the frame budget16.7 ms per frame at 60 Hz, 8.3 ms at 120 Hz; less is available in practice
Use will-change sparinglyit promotes a layer, which costs memory; add it just before animating
Keep Lottie files lightcomplex After Effects compositions become heavy JSON; test on low-end devices
Measurebrowser DevTools Performance panel; Xcode Instruments on iOS; Android's GPU rendering profiler and Perfetto

The rendering pipeline behind these rules is covered in CSS animation.

Reduced motion

A vestibular disorder affects the inner-ear balance system; for people who have one, on-screen motion can cause dizziness, nausea and trouble concentrating. Parallax, large slides, zooms, spinning and anything that moves on its own are the usual triggers.

PlatformUser settingHow code reads it
WebOS reduce-motion settings (macOS, iOS, Windows animation effects, Android)CSS @media (prefers-reduced-motion: reduce); matchMedia(); Motion's MotionConfig reducedMotion="user"; GSAP's gsap.matchMedia()
iOSSettings → Accessibility → Motion → Reduce MotionUIAccessibility.isReduceMotionEnabled; SwiftUI's accessibilityReduceMotion environment value
AndroidAccessibility → Remove animations (wording varies by version)sets the system animation scale to zero; check custom animations honor it (ValueAnimator.areAnimatorsEnabled())

What a reduced-motion version should do: replace movement with a short crossfade or an instant change; remove parallax, zooms, large slides, autoplay and scroll-jacking; keep small changes that carry meaning (color, opacity, a progress bar); keep feedback. WCAG requirements: anything that moves on its own for more than 5 seconds needs pause, stop or hide (2.2.2, A); nothing flashes more than 3 times a second (2.3.1, A); motion triggered by interaction can be turned off (2.3.3, AAA).

Implementation vocabulary

ToolWhat it isPick it forAsk for it like
CSS transitionsanimate a property when its value changeshover, focus, toggles, simple state changes"CSS transitions on transform and opacity using our tokens"
CSS keyframesnamed multi-step animations (@keyframes)loaders, entrances on load, loops"A keyframe animation with a reduced-motion override"
@starting-styleCSS for an element's first frame, so entry from display: none can transitiondialogs and popovers appearing"Entry and exit for the dialog with @starting-style"
Web Animations APIelement.animate(): CSS-grade animations controlled from JavaScript, with pause, reverse, seek and promisesvalues computed at runtime, FLIP, sequencing"WAAPI, awaiting finished between steps"
View Transitions APIthe browser snapshots old and new states and animates between themroute changes, shared elements on the web"A view transition with the image as a named shared element"
Scroll-driven animationsanimation tied to a scroll or view timelineprogress bars, reveals; not supported in every browser yet"A scroll-driven progress bar with a static fallback"
Motion (formerly Framer Motion)animation library for React and JavaScript: springs, exit animations, layout and shared-element animation, gesturesReact apps"Motion for React with AnimatePresence and layoutId"
GSAPJavaScript timeline library with ScrollTrigger and SVG tools; free since version 3.13long sequenced timelines, marketing pages"A GSAP timeline inside gsap.matchMedia()"
Lottiea JSON animation format exported from After Effects (via the Bodymovin plugin), with players for web, iOS and Androidillustrations and icons animated by designers"A Lottie brief: size, frame rate, loop, segments to play"
Rivean editor and runtimes for interactive vector animation driven by state machinesanimations that react to input: toggles, characters, mascots"A Rive state machine with inputs hover, pressed, success"
SwiftUI / UIKitwithAnimation, springs, matchedGeometryEffect; UIViewPropertyAnimator for interruptible UIKit animationiOS and iPadOS"SwiftUI with .spring(duration: 0.35, bounce: 0.15)"
Jetpack Composeanimate*AsState, AnimatedVisibility, updateTransition, spring(), shared elementsAndroid"Compose AnimatedVisibility with a spring, damping 0.8"

Brief the model

Each input has a slot in the prompt skeleton from fundamentals; the Slot column says where it goes.

InputWhy it mattersExampleSlot
Existing tokensconsistency"Durations: 150/250/400 ms; easing tokens: [paste]"<materials>
The state change and triggermotion explains a change; the model needs both ends"Tap a card in the grid; detail view opens"<context>
Purposedecides the pattern"Keep users oriented: they should see where the card went"<context>
Platform and stackAPIs and idioms differ"Next.js + Motion for React" or "SwiftUI, iOS 17+"<context>
Personalitysets bounce and duration"Calm and precise; no bounce except on success"<context>
Frequencyfrequent actions must be faster"Opened 50 times a day"<context>
Distance and sizebigger moves need more time"Full-screen on phone, 480 px panel on desktop"<context>
Motion you like, and one you don'tfeel is easier to show than to describe"A gallery app's zoom into a photo: quick, no bounce. Our current modal: slow and floaty"<examples>
Interruptionwhat happens if the user acts mid-animation"Swipe down to dismiss at any point"<requirements>
Devices and performancebudgets differ"Must be smooth on a 3-year-old mid-range Android"<requirements>
Accessibilityreduced motion and focus behavior"Reduced motion: crossfade only; focus moves immediately"<requirements>
Outputwhat you'll do with it"Spec table first, then CSS, then a reduced-motion variant"<output_format>

What to ask for

StageNamed deliverables
Systema one-page motion principles doc; motion tokens (durations, easings, springs) with usage rules; a pattern library (which transition for which navigation)
Designa motion spec table per component or transition; a storyboard with timestamps; a state diagram showing which transition plays between states; reduced-motion variants
Prototypean HTML/CSS or Motion prototype with a slow-motion toggle; a brief for a Lottie or Rive file
Buildproduction code for your stack using the tokens; interruption handling; reduced-motion handling; tests that animations end in the right state
Reviewa motion audit of an existing app (purpose, duration, easing, performance, accessibility); a performance check; a QA checklist

The prompt chain

The main deliverable is a motion spec for one app interaction: the timing table, easing, choreography and reduced-motion fallback, then the code that implements it. Run stages 1–3 in one chat. Run stage 4 in a fresh chat, because a model reviewing its own draft shares its blind spots.

1. Interview

Use first, when you know the interaction but not what a motion designer needs before speccing it.

<context>
I need a motion spec, then code, for one interaction in my app: [interaction, e.g. tapping a product card opens its detail page].
Stack: [stack: CSS | Web Animations API | View Transitions API | Motion for React | SwiftUI | Jetpack Compose].
I am not a motion designer.
</context>
 
<task>
Interview me first: ask the questions a motion designer asks before writing a motion spec.
</task>
 
<requirements>
- At most 10 questions, grouped under: the state change, purpose, frequency and context, gestures and interruption, accessibility, the existing motion system.
- For each question: why it matters to the spec, and 2-3 typical answers I can pick from.
- Do not propose any motion, numbers or code yet.
</requirements>
 
<process>
Ask the questions, then stop and wait for my answers.
</process>

2. Options

Use once the interview is answered, to see materially different patterns before committing to one.

<materials>
[paste the interview questions and my answers]
</materials>
 
<task>
Propose 2-4 materially different motion approaches for this interaction, e.g. a container transform, a shared-element transition, a shared axis or a fade through.
</task>
 
<requirements>
- For each: the pattern by name, the lead element, a duration range and easing family, what it explains to the user (orientation, continuity, hierarchy), the effort to build it on my stack, its performance risk on a low-end phone, and its reduced-motion version.
- Say which Material 3 transition pattern or Apple Human Interface Guidelines convention each follows, and where it departs from it.
- Recommend one and say what would make you pick another.
</requirements>
 
<process>
Give the options as a table, then the recommendation, then stop and wait for my choice. Do not write the spec yet.
</process>

3. Produce

Use after you have chosen: the full skeleton, filled in for the chosen pattern.

<materials>
Motion tokens: [paste, e.g. durations 150/250/400 ms; standard easing cubic-bezier(0.2, 0, 0, 1)]
Interview answers and the option I chose: [paste]
</materials>
 
<context>
Interaction: [interaction, e.g. tapping a product card in a grid opens its detail page]
Purpose: [purpose: feedback | orientation | continuity | hierarchy | status | delight]
Chosen pattern: [pattern: container transform | shared element | shared axis | fade through | crossfade]
Stack: [stack: CSS | Web Animations API | View Transitions API | Motion for React | SwiftUI | Jetpack Compose]
Frequency: [frequency: dozens of times a day | a few times a day | once per session | rarely]
The spec goes to engineers who will implement it exactly as written.
</context>
 
<examples>
[optional: 1-2 interactions in other apps whose motion you like and why, e.g. "a gallery app's zoom into a photo: quick, no bounce, I never lose my place"; 1 you don't, e.g. "our current modal: 600 ms and floaty"]
</examples>
 
<task>
A motion spec for this interaction, then production code that implements it, so engineers can build it without guessing numbers.
</task>
 
<requirements>
- Durations within the usual ranges (feedback 50-150 ms, menus 150-250 ms, dialogs and sheets 250-400 ms, screens 300-500 ms); justify anything outside them.
- Easing by direction: entering elements decelerate, leaving elements accelerate, on-screen moves use standard easing. Give exact cubic-bezier values or spring parameters (damping ratio and stiffness), using my tokens where they exist.
- The exit is faster than the entry, not the same animation reversed.
- Animate only transform and opacity unless you justify the exception; never scale from 0.
- Interruptible: a tap or swipe mid-transition reverses from the current position and velocity.
- Holds 60 fps on [devices, e.g. a 3-year-old mid-range Android phone].
- Focus moves immediately, input is never blocked, and the end state is correct even if the animation never runs.
- Reduced motion (prefers-reduced-motion on the web, Reduce Motion on iOS, Remove animations on Android): replace movement with a short crossfade or an instant change; keep feedback.
- Name the Material 3 pattern or Apple HIG convention you follow and where you depart from it.
- Never invent numbers, names, quotes or sources: write [NEEDS SOURCE] where support is missing, and [CHECK DOCS] next to any API you are not sure exists in the current version.
- End with a list of the assumptions you made.
</requirements>
 
<quality_bar>
Before answering, check your draft against:
- Every animated element serves the stated purpose; anything that doesn't is cut.
- One lead element; the others follow it; any stagger is capped at about 300 ms in total.
- Every row has a duration, a delay and an easing or spring in numbers; no "smooth" or "snappy" without them.
- The code matches the spec table value for value, including the reduced-motion path and when focus moves.
- Nothing in the code animates width, height, top, left, margin or a large blur.
</quality_bar>
 
<output_format>
1. A spec table: element | property | from | to | duration (ms) | delay (ms) | easing or spring | notes.
2. The choreography in one sentence, and how the reverse transition differs in one sentence.
3. Interruption, focus and reduced-motion rules as bullets, with the reduced-motion replacement for each row.
4. The code in one self-contained file.
5. The assumptions.
</output_format>
 
<process>
Ask me up to 3 questions first if anything missing would change your answer; otherwise go on.
Write the spec table first and check it against the quality bar before writing any code.
</process>

4. Review

Use when the spec and code are back: open a fresh chat, paste the brief and the output, and ask for a ranked critique, not a rewrite.

<materials>
The brief: [paste the stage 3 prompt]
The spec and code: [paste]
</materials>
 
<context>
This is a fresh chat. The spec below was written in another conversation; review it as someone seeing it for the first time, before it goes to engineering.
</context>
 
<task>
A review of this motion spec and its code. Do not rewrite them.
</task>
 
<requirements>
- Judge against: purpose, duration ranges, easing direction, choreography, interruption, performance (transform and opacity only), reduced motion, consistency with the tokens, accessibility (focus, input, flashing; WCAG 2.2.2, 2.3.1 and 2.3.3), and a correct end state without the animation.
- Walk through it three times: as a user with reduced motion on, as a keyboard user, and on a slow phone that drops frames. Say what breaks in each.
- Compare the code with the spec table row by row.
- Quote the row or line behind every finding.
</requirements>
 
<quality_bar>
Before answering, check that every finding names a criterion, cites a row or line, and gives a fix in numbers or code terms.
</quality_bar>
 
<output_format>
A table: rank | finding | criterion | where | fix, most severe first. Then one line: ship, fix first, or redo.
</output_format>
 
<process>
Rank the issues by impact on users; do not produce a revised spec or code.
</process>

Goals and loops

In an agent that runs commands and checks its own work, such as Claude Code, the Produce stage can run until it measurably passes: /goal keeps starting turns until a condition holds, and /loop repeats a prompt or skill on an interval. The checker reads only the conversation, so every condition below names a check whose output Claude prints, the constraints that must survive, and a bound. How to write your own: Goals and loops.

TaskCompletion condition
Implement the motion spec/goal npx playwright test motion.spec.ts passes, including a run with reduced motion emulated in which nothing moves position, and every duration matches its token in the spec (print each); or stop after 15 turns
Performance-safe animations/goal npx stylelint "src/**/*.css" with stylelint-high-performance-animation reports 0 problems, so only transform and opacity animate (show its output); or stop after 10 turns
Motion tokens/goal tokens.json defines every duration and easing named in the spec, and a search of the code for raw millisecond values in transitions finds 0 (print the count); or stop after 8 turns

Loops that fit: rarely: motion work is short and visual. Use /loop only to watch a long performance or visual-regression run in CI.

What a checker can't judge: how it feels. Watch it at full speed, slowed down and on a slow phone, with reduced motion on and off.

Prompt library

One entry per deliverable a motion designer produces, besides the spec in the chain. Quick is for when you're in a hurry; Full is for when the result matters.

Motion tokens

Stage: produce · Use when: you're starting a design system, or every screen animates differently.

Quick

Create motion tokens for [product, e.g. a calm budgeting app] on [stack: CSS | Motion for React | SwiftUI | Jetpack Compose]: 5-7 durations in ms, easings as cubic-bezier (standard, decelerate, accelerate, one emphasized pair), 3 spatial springs and 1 effects spring, and which token to use for press, menus, dialogs, sheets and page transitions.
Base the ranges on Material 3 and NN/g, and check that frequent actions stay under about 200 ms.

Full

<materials>
[optional: paste the animation values the app uses today, or a list of its animations]
</materials>
 
<context>
Product: [product, e.g. a budgeting app for freelancers]
Personality: [personality: calm and precise | brisk and efficient | playful | expressive]
Platforms: [platforms, e.g. web with CSS and Motion for React; iOS with SwiftUI]
The tokens will be used by [team, e.g. 3 engineers and no motion designer].
</context>
 
<examples>
[optional: 1-3 apps whose motion feels right for us and why, e.g. "fast, never bounces, things leave quicker than they arrive"; 1 that feels wrong, and why]
</examples>
 
<task>
A set of motion tokens with usage rules, so every component animates from the same values.
</task>
 
<requirements>
- Duration tokens: 5-7 steps in ms, each with what it is for.
- Easing tokens as cubic-bezier values: standard, enter (decelerate), exit (accelerate), and one emphasized pair.
- Spring tokens: 3 spatial (fast, default, slow) and 1 effects spring with no overshoot, as damping ratio and stiffness, plus the equivalent in each of my platforms' own parameters.
- Usage rules: which tokens for press feedback, menus, dialogs, sheets, page transitions and staggers.
- A reduced-motion rule for each category.
- Base the ranges on Material 3's motion tokens and NN/g's duration guidance, and say where you deviate and why.
- Never invent numbers, names, quotes or sources: write [NEEDS SOURCE] where support is missing.
- End with a list of the assumptions you made.
</requirements>
 
<quality_bar>
Before answering, check your draft against:
- Frequent actions (press, toggle, menu) stay at or under about 200 ms.
- Every exit token is shorter than its matching enter token.
- No effects token (color, opacity) overshoots.
- The personality shows in the numbers (bounce, duration), not in adjectives.
</quality_bar>
 
<output_format>
Tables for durations, easings and springs (name | value | use for), the usage rules as a table, then the tokens as CSS custom properties, plus a constants file for each native platform I listed.
</output_format>
 
<process>
Ask me up to 3 questions first if anything missing would change your answer; otherwise go on.
</process>

Micro-interaction

Stage: produce · Use when: a button, toggle or like feels dead, or users tap twice because nothing seems to happen.

Quick

Spec and code a press micro-interaction for [component, e.g. the primary button] in [stack: CSS | Motion for React | SwiftUI | Jetpack Compose]: scale to about 0.97 in about 100 ms, spring back with no visible bounce, the same pressed state on Space and Enter, color change only under reduced motion.
Check that feedback starts within 100 ms and the action never waits for the animation.

Full

<materials>
Tokens: [paste, or write "none yet"]
</materials>
 
<context>
Component: [component: primary button | toggle | favorite or like | checkbox | add to cart]
What it confirms: [result, e.g. "added to favorites", saved optimistically before the server answers]
Stack: [stack: CSS | Motion for React | SwiftUI | Jetpack Compose]
Frequency: [frequency: dozens of times a day | a few times a day | rarely]
</context>
 
<examples>
[optional: a micro-interaction you like and why, e.g. "a like that fills and settles in under 200 ms"; one you don't, e.g. "a 600 ms bounce on every tap"]
</examples>
 
<task>
A micro-interaction spec and code for this component: rest, hover, pressed, success, failure and disabled states.
</task>
 
<requirements>
- Press feedback within about 100 ms: a slight scale (about 0.97) or one color step; never scale from 0.
- Release springs back with no visible overshoot; a success state may overshoot once, on spatial properties only.
- Hover only on devices that support it (@media (hover: hover)); the same pressed state on Space and Enter.
- Optimistic: show the result at once and animate a rollback if the request fails.
- Reduced motion: color or opacity change only; keep the feedback.
- Animate only transform and opacity.
- Never invent numbers, names, quotes or sources: write [NEEDS SOURCE] where support is missing.
- End with a list of the assumptions you made.
</requirements>
 
<quality_bar>
Before answering, check your draft against:
- It still feels instant on the 50th press, not just the first.
- The action never waits for the animation to finish.
- Every state has a duration and an easing or spring in numbers.
</quality_bar>
 
<output_format>
A spec table (state | property | from | to | duration | easing or spring), then the code.
</output_format>
 
<process>
Ask me up to 3 questions first if anything missing would change your answer; otherwise go on.
</process>

Storyboard

Stage: produce · Use when: you need to see or explain a transition before anyone builds it.

Quick

Storyboard the transition from [screen A, e.g. the inbox] to [screen B, e.g. one message] and back as 6-8 key frames each way: timestamp (ms), what's on screen, what moves and which way, and where the eye should be.
Flag any frame where two things compete for attention.

Full

<materials>
[paste or describe both screens: layout and main elements]
</materials>
 
<context>
Screens: from [screen A, e.g. the inbox list] to [screen B, e.g. one message], and back.
User and moment: [user, e.g. a commuter on a phone, one hand, going back and forth often]
Pattern so far: [pattern: container transform | shared element | shared axis | fade through | not decided]
The storyboard is for [readers, e.g. a designer who will animate it and a product lead who must approve it].
</context>
 
<task>
A storyboard of the transition in both directions, so it can be reviewed before it is built.
</task>
 
<requirements>
- 6-8 key frames per direction, each with a timestamp (ms), what is on screen, what is moving and in which direction, and where the eye should be.
- One lead element; say when each other element starts relative to it.
- Keep to the usual range for screen transitions (about 300-500 ms) and name the easing for each movement.
- Then narrate the same transition at 10% speed, as if describing a slow-motion recording.
- Describe the reduced-motion version in one line.
- Never invent numbers, names, quotes or sources: write [NEEDS SOURCE] where support is missing.
- End with a list of the assumptions you made.
</requirements>
 
<quality_bar>
Before answering, check your draft against:
- At every frame there is one obvious place to look.
- Spatial logic holds: forward and back move in opposite directions, and things leave toward where they live.
- The back transition is shorter than the forward one.
- Nothing slides or zooms across the full screen without a reason the user would notice.
</quality_bar>
 
<output_format>
A table per direction (frame | ms | on screen | moving | eye on), the slow-motion narration in under 150 words, the reduced-motion line, then the assumptions.
</output_format>
 
<process>
Ask me up to 3 questions first if anything missing would change your answer; otherwise go on.
</process>

Gesture-driven component

Stage: produce · Use when: a sheet, drawer, swipeable card or carousel should follow the finger and settle naturally.

Quick

Implement a bottom sheet in [stack: Motion for React | SwiftUI | Jetpack Compose] with snap points at [snap points, e.g. 40% and 90% of screen height]: it follows the finger 1:1, rubber-bands past the top, settles with a spring that carries the release velocity, and dismisses past [threshold, e.g. 30% down or a fast fling].
Add a close button as the non-drag alternative, and check that a new touch catches it mid-flight.

Full

<context>
Component: [component: bottom sheet | side drawer | swipeable card | carousel]
Stack: [stack: Motion for React | SwiftUI | Jetpack Compose | UIKit]
Snap points: [snap points, e.g. 40% and 90% of screen height]
Dismiss rule: [rule, e.g. dragged past 30% down, or flung down fast]
Frequency: [frequency: dozens of times a day | a few times a day | rarely]
</context>
 
<task>
A gesture-driven version of this component, with a behavior spec and code: drag, release, snap, dismiss and interruption.
</task>
 
<requirements>
- While dragging: direct manipulation; the element tracks the finger exactly, with no easing.
- Rubber banding past the outermost snap point, with increasing resistance.
- On release: pick the snap point from position plus velocity; a spring (damping ratio about 0.85) starts with the release velocity.
- Interruptible: a new touch catches the element mid-animation.
- The backdrop's opacity tracks the position.
- A tap or button alternative for every drag (WCAG 2.5.7 Dragging Movements).
- Reduced motion: no overshoot and a shorter settle; the gesture still works.
- Spring parameters in the platform's own terms: [spring parameters: dampingRatio and stiffness | duration and bounce | stiffness, damping and mass].
- Never invent numbers, names, quotes or sources: write [NEEDS SOURCE] where support is missing, and [CHECK DOCS] next to any API you are not sure exists in the current version.
- End with a list of the assumptions you made.
</requirements>
 
<quality_bar>
Before answering, check your draft against:
- A slow drag and a fast fling from the same position can land on different snap points.
- Nothing jumps when the finger lifts: the spring starts from the current position and speed.
- The non-drag alternative reaches every snap point.
- Only transform and opacity animate.
</quality_bar>
 
<output_format>
A behavior table (phase | rule | numbers), one self-contained code example, then what to test on a real device.
</output_format>
 
<process>
Ask me up to 3 questions first if anything missing would change your answer; otherwise go on.
</process>

Loading and arrival

Stage: produce · Use when: content takes 1–10 seconds to load, the screen looks frozen or flashes a spinner, or a list appears all at once.

Quick

Design the loading state for [screen, e.g. the order history list]: a skeleton that matches the final layout exactly, a slow low-contrast shimmer (static under reduced motion), nothing at all if data arrives within about 300 ms, then a 150 ms crossfade or a 30 ms stagger capped at 6 items.
Check that nothing shifts when the content lands.

Full

<materials>
[paste the screen's layout or component code]
</materials>
 
<context>
Screen: [screen, e.g. the order history list, up to 50 items]
Typical wait: [wait: under 1 s | 1-10 s | over 10 s | varies a lot]
Stack: [stack: CSS | Motion for React | SwiftUI | Jetpack Compose]
</context>
 
<task>
The loading and arrival motion for this screen: what shows while waiting, and how the content enters when it arrives.
</task>
 
<requirements>
- Match the indicator to the wait, following NN/g: nothing under about 1 s, a skeleton for full-page loads up to about 10 s, a determinate progress bar with an estimate beyond that.
- Skeleton: the same heights and positions as the final layout; a slow, subtle shimmer about every 1.5 s.
- Don't show the skeleton at all if data arrives within about 300 ms.
- Arrival: a 150 ms crossfade, or items fade in and rise 8 px over 200 ms with a 30 ms stagger for the first 6 visible items; the rest appear with the sixth.
- Never re-run the entrance on re-render, filtering or pagination.
- Reduced motion: a static skeleton and a single short fade on arrival.
- Mark the region aria-busy while loading.
- Never invent numbers, names, quotes or sources: write [NEEDS SOURCE] where support is missing.
- End with a list of the assumptions you made.
</requirements>
 
<quality_bar>
Before answering, check your draft against:
- Nothing moves or resizes when content replaces the skeleton (no layout shift).
- The whole entrance is finished within about 350 ms.
- A fast connection never sees a flash of skeleton.
</quality_bar>
 
<output_format>
A short spec table, the code, then how you stop the entrance from re-running.
</output_format>
 
<process>
Ask me up to 3 questions first if anything missing would change your answer; otherwise go on.
</process>

Lottie or Rive brief

Stage: produce · Use when: an illustration, icon or mascot will be animated by a designer, and you need to brief them and the engineers who trigger it.

Quick

Write a brief for a [format: Lottie | Rive] animation of [subject, e.g. an empty-inbox illustration]: size, frame rate, duration, loop or play once, the segments or states the app triggers, a file-size budget, and the still frame for reduced motion.
Check that every state the app needs is named.

Full

<materials>
[paste the palette and illustration style rules]
</materials>
 
<context>
Where it appears: [placement, e.g. the inbox empty state, 240 x 240 px on mobile]
What it should do: [purpose: feedback | status | delight | onboarding]
Format: [format: Lottie | Rive]
Behavior: [behavior: plays once | loops | segments triggered by the app | a state machine reacting to input]
Platforms: [platforms, e.g. web, iOS and Android]
</context>
 
<examples>
[1-3 animations you like and why, e.g. "a checkmark that draws in, then settles, in under 1 s"; 1 you don't, and why]
</examples>
 
<task>
A brief an animator can work from and an engineer can integrate against, for this animation.
</task>
 
<requirements>
- Specs: artboard size, frame rate, duration, loop or play once, and either the named segments (Lottie) or the state machine's inputs and states (Rive) the app will trigger.
- Motion direction in plain words and numbers: easing family, amount of overshoot, and which principles carry the feel (anticipation, follow-through, squash and stretch).
- Performance: a file-size budget, a layer limit, no features the target players don't support (check the player's supported-features list), and a test on a low-end phone.
- Reduced motion: the still frame to show, and whether it may play once on request.
- Anything that loops for more than 5 seconds needs a pause control (WCAG 2.2.2); nothing flashes more than 3 times a second (2.3.1).
- Never invent numbers, names, quotes or sources: write [NEEDS SOURCE] where support is missing.
- End with a list of the assumptions you made.
</requirements>
 
<quality_bar>
Before answering, check your draft against:
- An animator could start without asking a question, and the engineer knows exactly which segments or inputs to call.
- Every state the app needs (idle, hover, pressed, success, error, loading) is named or explicitly ruled out.
- The motion direction uses numbers, not "fun" or "smooth".
</quality_bar>
 
<output_format>
A one-page brief: purpose, a specs table, a states or segments table (name | trigger | frames or behavior), motion direction, performance budget, accessibility, deliverables (source file, exported file, still frame).
</output_format>
 
<process>
Ask me up to 3 questions first if anything missing would change your answer; otherwise go on.
</process>

Translate a feeling into numbers

Stage: options · Use when: a stakeholder says "snappier" or "more playful" and you need concrete parameters to try.

Quick

Our [component, e.g. sheet-open] animation uses [values, e.g. 400 ms, cubic-bezier(0.4, 0, 0.2, 1)]. It should feel [direction: snappier | heavier | calmer | more playful].
Give 3 variants, each with duration or spring parameters and one sentence on how it will feel; no overshoot on color or opacity. Stop after the options.

Full

<materials>
Current values: [paste, e.g. 400 ms, cubic-bezier(0.4, 0, 0.2, 1), or the code]
Tokens: [paste, or write "none"]
</materials>
 
<context>
Component: [component, e.g. the bottom sheet opening]
Feedback we got: [quote, e.g. "it feels sluggish"]
Direction: [direction: snappier | heavier | calmer | more playful | more premium]
Stack: [stack: CSS | Motion for React | SwiftUI | Jetpack Compose]
</context>
 
<examples>
[optional: 1-2 interactions that already have the feel we want and why; 1 that goes too far]
</examples>
 
<task>
Three variants that move this animation in the requested direction, so we can try them side by side and pick one.
</task>
 
<requirements>
- Each variant: duration and easing, or spring parameters as damping ratio and stiffness, plus the equivalent in my stack's own parameters.
- Vary them materially, e.g. a shorter duration, a different curve, a spring with slight overshoot.
- One sentence each on how it will feel different and what it costs (overshoot reads as playful but distracts on frequent actions).
- No overshoot on color or opacity; stay inside our tokens or say which new token a variant needs.
- Never invent numbers, names, quotes or sources: write [NEEDS SOURCE] where support is missing.
- End with a list of the assumptions you made.
</requirements>
 
<quality_bar>
Before answering, check your draft against:
- The three variants would look different side by side, not 10 ms apart.
- Each one stays inside the duration range for this kind of motion.
- The recommendation says what to test it against, e.g. the 50th repetition and a low-end phone.
</quality_bar>
 
<output_format>
A table: variant | values | stack equivalent | how it feels | risk. Then a recommendation in two sentences.
</output_format>
 
<process>
Give the options and the recommendation, then stop and wait for my choice before writing any code.
</process>

Motion audit

Stage: review · Use when: an existing app feels slow, janky or inconsistent, or before a release with significant animation.

Quick

Audit the motion in this [input: code | screen recording described frame by frame | list of animations]: for each animation, its duration, easing, purpose and problems (too long, wrong easing direction, layout properties animated, not interruptible, no reduced-motion path). Rate severity 1-3, list the top 5 fixes, and don't rewrite anything.
[paste]

Full

<materials>
[paste the code, or describe the screen recording frame by frame with timestamps]
</materials>
 
<context>
App: [app, e.g. a React web app using Motion, used all day by support agents]
Complaint: [complaint: feels slow | janky on some devices | inconsistent between screens | an accessibility complaint]
Target devices: [devices, e.g. mid-range Android phones and 5-year-old laptops]
</context>
 
<task>
A motion audit of this app, covering purpose, performance, accessibility and consistency. Do not rewrite the code.
</task>
 
<requirements>
- For each animation: what animates, its duration and easing, its purpose (feedback, orientation, continuity, hierarchy, status, delight or none), and its problems.
- Check: durations against the usual ranges; easing direction; linear movement; layout properties animated (width, height, top, left, margin); large blurs or shadows; will-change left on permanently; animations that can't be interrupted; competing movements; focus or input blocked during animation.
- Accessibility: a reduced-motion path for each animation; anything moving on its own for more than 5 seconds without a pause (WCAG 2.2.2); anything that could flash more than 3 times a second (2.3.1); parallax, zooms or large slides (vestibular triggers); drags without a tap alternative (2.5.7).
- Quote the line or timestamp behind every finding.
- Never invent numbers, names, quotes or sources: write [NEEDS SOURCE] where support is missing.
- End with a list of the assumptions you made.
</requirements>
 
<quality_bar>
Before answering, check your draft against:
- Every finding names the rule it breaks and where.
- Severity reflects frequency: a slow animation on a frequent action outranks a slow one on a rare screen.
- The fixes converge on one shared set of tokens, not a new value per component.
</quality_bar>
 
<output_format>
A table: issue | where | rule (WCAG criterion, performance or guideline) | severity 1-3 | fix. Then the top 5 changes by impact, and a proposed shared token set in one short table.
</output_format>
 
<process>
Ask me up to 3 questions first if anything missing would change your answer; otherwise go on. List the fixes; don't apply them.
</process>

Worked example

Weak prompt

Make the modal animate nicely.

What's wrong

  • No platform, trigger, purpose or frequency, so the model can't choose durations: a dialog seen 20 times a day needs different motion from a one-time welcome screen.
  • "Nicely" is a feeling, not a spec: no durations, easing or tokens to match.
  • Nothing about interruption, focus or reduced motion, so none of them will be handled.
  • No output shape: you get code with no spec to check it against.
  • Typical result: a 600 ms bounce-in with ease-in-out, scaling from 0, animating top and height, the same animation reversed for exit, focus moved only after it finishes, no reduced-motion version, and code that snaps back at the end.

Strong prompt

<materials>
Motion tokens: durations 150 / 250 / 400 ms; enter easing cubic-bezier(0, 0, 0, 1); exit easing cubic-bezier(0.3, 0, 1, 1).
</materials>
 
<context>
A confirmation dialog in a React app using Motion for React. Trigger: the user clicks "Delete project". Power users see it several times a day, so it must feel quick.
Purpose: focus attention on the decision and show that the dialog sits above the page.
</context>
 
<task>
The open and close motion for this dialog: a spec table, then the component code.
</task>
 
<requirements>
- Open: the backdrop fades in; the dialog fades in and scales from 0.96 to 1, never from 0. Close: faster than open.
- Transform and opacity only.
- Focus moves into the dialog as soon as it opens, not when the animation ends; Esc and a backdrop click close it; closing mid-open reverses smoothly from where it is.
- Reduced motion: opacity only, 150 ms.
- Never invent numbers, names, quotes or sources: write [NEEDS SOURCE] where support is missing, and [CHECK DOCS] next to any Motion API you are not sure exists.
- End with a list of the assumptions you made.
</requirements>
 
<quality_bar>
Before answering, check your draft against:
- Durations fit a frequent dialog: open about 250 ms or less, close shorter.
- Enter decelerates, exit accelerates.
- The code matches the spec table value for value, including when focus moves and the reduced-motion path.
</quality_bar>
 
<output_format>
A spec table (element | property | from | to | duration | delay | easing), then one component file.
</output_format>
 
<process>
Ask me up to 3 questions first if anything missing would change your answer; otherwise go on. Write the spec first and check it against the quality bar before writing code.
</process>

What came back

Illustrative, abridged:

element   property  from   to    duration  delay  easing
backdrop  opacity   0      1     250 ms    0      cubic-bezier(0, 0, 0, 1)
dialog    opacity   0      1     250 ms    0      cubic-bezier(0, 0, 0, 1)
dialog    scale     0.96   1     250 ms    0      cubic-bezier(0, 0, 0, 1)
close     all rows reversed      150 ms    0      cubic-bezier(0.3, 0, 1, 1)
Reduced motion: opacity only, 150 ms. Focus: moves to Cancel on open.
 
<motion.div role="alertdialog" aria-modal="true"
  initial={{ opacity: 0, scale: reduce ? 1 : 0.96 }}
  animate={{ opacity: 1, scale: 1 }}
  exit={{ opacity: 0, scale: reduce ? 1 : 0.96,
          transition: { duration: 0.15, ease: [0.3, 0, 1, 1] } }}
  transition={{ duration: reduce ? 0.15 : 0.25, ease: [0, 0, 0, 1] }}
  onAnimationComplete={() => cancelRef.current?.focus()}>
…
Assumptions: Cancel is the safer default focus; the backdrop is a sibling motion.div inside AnimatePresence.

How an expert judged it

  • Durations and easing: pass. 250 ms open with a decelerating curve, 150 ms close with an accelerating one, and a scale from 0.96 suit a dialog seen several times a day.
  • Reduced motion: pass. Scale is pinned to 1, so only opacity changes.
  • Code matches the spec: fail. The spec says focus moves on open; the code moves it in onAnimationComplete, 250 ms later. Keyboard and screen-reader users wait, and a quick Enter press lands on the page behind the dialog. The demo looks perfect, which is why this is caught by reading the code against the spec row by row, the third check in the quality bar.
  • Interruption: not shown. The output doesn't say what happens when Esc is pressed 100 ms into the open; ask for a test rather than trust the assumption.

The follow-up

The spec says focus moves into the dialog on open, but the code focuses Cancel in onAnimationComplete, which runs 250 ms later. Fix only that: move focus to Cancel as soon as the dialog mounts, return focus to the "Delete project" button on close, and leave every motion value unchanged. Then show a test that presses Esc 100 ms after opening and checks that the dialog reverses from where it is and focus returns to the button.

Judge the output

CriterionGoodRed flag
Purposeeach animation explains somethingmotion for its own sake on a frequent action
Durationwithin the ranges above; frequent actions near-instant600 ms+ for a menu or dialog
Easing directionenter decelerates, exit accelerates, on-screen moves use standardlinear movement; ease-in on entry
Choreographyone lead movement; staggers cappedeverything moves at once; a 2-second stagger
Interruptionreverses from the current state; springs for gesturesmust finish before the next tap works
Performancetransform and opacity onlyanimating height, top, box-shadow or large blurs
Reduced motiona defined alternative per animationnone, or all feedback removed
Consistencytokens reused across componentsa new duration and curve per component
Accessibilityfocus moves immediately; input never blocked; no flashingfocus waits for the animation; content hidden until animation ends
End statecorrect without the animation (no snap-back)relies on fill: forwards or a final callback

The same checks are built into every <quality_bar> on this sheet, so the model applies them before you see a draft; use the table to check what comes back.

Check your spec against these ranges: feedback 50-150 ms,
menus 150-250 ms, dialogs and sheets 250-400 ms, screens
300-500 ms. Flag anything outside them and justify or fix it.
Then list every property you animate that isn't transform or
opacity.

Pitfalls

PitfallWhy it happensFix
Too slowlong motion looks smooth in a demouse the duration table; test the 50th repetition, not the first
Linear or wrong-direction easingdefaultsname the easing token per movement
Scaling from 0"pop in" requestsstart at 0.9–0.97 scale plus opacity
Animating layout propertiesthe obvious property is heighttransform, FLIP, or the View Transitions API
Same animation for exitreversing is easyexits shorter and accelerating
Not interruptiblefixed-duration animations queuedsprings; retarget from the current value
Blocking input or focuswaiting for finishedmove focus and accept input immediately
No reduced-motion versionnot requestedask for it in every prompt
Everything bounces"make it playful"overshoot only on spatial springs and only where it means something
Vague words"snappy", "smooth", "subtle"ask for milliseconds, cubic-bézier values or spring parameters
Trusting API detailslibraries change fastcheck current docs for Motion, GSAP, SwiftUI or Compose
Heavy Lottie filescomplex After Effects exportssimplify layers; test on low-end devices; consider CSS for simple icons

References