Documentation

Syntax Reference

Complete reference for all motionwind animation classes, properties, transitions, and configuration options.

Syntax Reference

Every motionwind class follows a consistent pattern:

animate-{gesture}:{property}-{value}

Configuration classes (transition, viewport, drag) omit the gesture prefix:

animate-{config}-{value}

All classes must be inside a static className string literal on a lowercase HTML element.

Gesture prefixes

The gesture prefix determines when the animation runs. It maps directly to a Motion prop.

PrefixMotion propDescription
hoverwhileHoverWhile the pointer is over the element
tapwhileTapWhile the element is actively pressed
focuswhileFocusWhile the element has keyboard focus
inviewwhileInViewWhile the element is visible in the viewport
dragwhileDragWhile the element is being dragged
initialinitialThe starting state (before enter animation)
enteranimateThe target state for mount/entrance animation
exitexitThe state to animate to on unmount

Usage

Combine a gesture prefix with any property:

<div className="animate-hover:opacity-50 animate-tap:scale-90 animate-focus:rotate-5">
  Interactive element
</div>

Multiple properties under the same gesture are merged:

{/* Both scale and opacity apply on hover */}
<div className="animate-hover:scale-110 animate-hover:opacity-80">
  Combined hover effect
</div>

Properties

scale

Scales the element uniformly. The value represents a percentage: 110 means 1.1x.

<div className="animate-hover:scale-110">Scale to 110%</div>
<div className="animate-hover:scale-50">Scale to 50%</div>
<div className="animate-hover:scale-200">Scale to 200%</div>

scale-x

Scales along the horizontal axis only.

<div className="animate-hover:scale-x-120">Stretch horizontally to 120%</div>

scale-y

Scales along the vertical axis only.

<div className="animate-hover:scale-y-80">Squish vertically to 80%</div>

scale-z

Scales along the depth axis (3D). The value represents a percentage: 150 means 1.5x.

<div className="animate-hover:scale-z-150">Scale depth to 150%</div>

rotate

Rotates the element in degrees.

<div className="animate-hover:rotate-45">Rotate 45 degrees</div>
<div className="animate-hover:rotate-180">Rotate 180 degrees</div>
<div className="animate-hover:rotate-360">Full rotation</div>

rotate-x

Rotates around the horizontal axis (3D tilt forward/backward).

<div className="animate-hover:rotate-x-30">Tilt forward 30 degrees</div>

rotate-y

Rotates around the vertical axis (3D tilt left/right).

<div className="animate-hover:rotate-y-45">Tilt sideways 45 degrees</div>

skew

Skews the element uniformly (both axes) in degrees.

<div className="animate-hover:skew-12">Skew uniformly 12 degrees</div>

skew-x

Skews the element along the horizontal axis in degrees.

<div className="animate-hover:skew-x-12">Skew horizontally 12 degrees</div>

skew-y

Skews the element along the vertical axis in degrees.

<div className="animate-hover:skew-y-6">Skew vertically 6 degrees</div>

x

Translates the element horizontally in pixels.

<div className="animate-hover:x-10">Move 10px right</div>
<div className="animate-hover:x-100">Move 100px right</div>

y

Translates the element vertically in pixels.

<div className="animate-hover:y-10">Move 10px down</div>
<div className="animate-initial:y-40 animate-enter:y-0">Slide up on enter</div>

z

Translates the element along the depth axis (3D) in pixels.

<div className="animate-hover:z-50">Move 50px toward the viewer</div>
<div className="animate-hover:-z-20">Move 20px away from the viewer</div>

origin-x, origin-y, origin-z

Sets the transform origin for the element. origin-x and origin-y use a percentage scale (0-100 maps to 0-1), while origin-z is in pixels.

{/* Transform from top-left corner */}
<div className="animate-hover:scale-110 animate-hover:origin-x-0 animate-hover:origin-y-0">
  Scales from top-left
</div>

{/* Transform from center (default) */}
<div className="animate-hover:rotate-45 animate-hover:origin-x-50 animate-hover:origin-y-50">
  Rotates from center
</div>

{/* Set z origin for 3D transforms */}
<div className="animate-hover:rotate-x-30 animate-hover:origin-z-100">
  3D tilt with depth origin
</div>

perspective

Sets the CSS perspective for 3D transforms in pixels. Lower values create more dramatic 3D effects.

<div className="animate-hover:perspective-800 animate-hover:rotate-y-30">
  3D rotation with perspective
</div>

opacity

Sets the element's opacity. The value is a percentage: 0 means fully transparent, 100 means fully opaque.

<div className="animate-hover:opacity-50">Fade to 50% on hover</div>
<div className="animate-initial:opacity-0 animate-enter:opacity-100">Fade in on mount</div>

blur

Applies a Gaussian blur in pixels. Produces a CSS filter: blur(Npx) value.

<div className="animate-hover:blur-4">Blur by 4px on hover</div>
<div className="animate-initial:blur-10 animate-enter:blur-0">Un-blur on enter</div>

brightness

Adjusts the element's brightness. The value is a percentage: 100 is normal, 150 is brighter, 50 is darker.

<div className="animate-hover:brightness-120">Brighten on hover</div>

contrast

Adjusts the element's contrast. The value is a percentage: 100 is normal.

<div className="animate-hover:contrast-150">Increase contrast on hover</div>

saturate

Adjusts the element's color saturation. The value is a percentage: 100 is normal, 0 is grayscale.

<div className="animate-hover:saturate-150">Boost saturation on hover</div>
<div className="animate-initial:saturate-0 animate-enter:saturate-100">Fade from grayscale to color</div>

w

Animates the element's width in pixels.

<div className="animate-hover:w-300">Expand to 300px wide on hover</div>

h

Animates the element's height in pixels.

<div className="animate-hover:h-200">Expand to 200px tall on hover</div>

rounded

Animates the element's border-radius in pixels.

<div className="animate-hover:rounded-20">Round corners to 20px on hover</div>

Negative values

Prefix the property-value segment with a dash to use negative values:

<div className="animate-hover:-rotate-45">Rotate -45 degrees</div>
<div className="animate-hover:-x-20">Move 20px to the left</div>
<div className="animate-hover:-y-10">Move 10px upward</div>
<div className="animate-hover:-skew-x-6">Skew -6 degrees</div>

The negative sign comes after the colon and before the property name:

animate-hover:-rotate-45
             ^
             negative prefix

Arbitrary values

For any Motion property not covered by the built-in classes, use bracket syntax:

animate-{gesture}:[key=value]

The key is the exact Motion property name, and the value is passed as-is (numbers are auto-detected):

{/* Arbitrary transform property */}
<div className="animate-hover:[rotateZ=30]">Rotate on Z axis</div>

{/* Arbitrary string value */}
<div className="animate-hover:[backgroundColor=#ff0000]">Red on hover</div>

{/* Numeric value auto-detection */}
<div className="animate-hover:[z=50]">Move on Z axis</div>

This gives you escape-hatch access to any property that Motion supports, including ones that motionwind does not have dedicated class names for.

Unit values

Many positional and sizing properties support unit suffixes. The following properties accept unit suffixes: x, y, z, w, h, top, left, right, bottom, p, m, gap, text-size, tracking, and leading.

Supported unit suffixes:

SuffixCSS unitExample classOutput value
pct%x-50pct"50%"
pxpxx-100px"100px"
vhvhy-50vh"50vh"
vwvwx-100vw"100vw"
remremgap-2rem"2rem"
ememp-1em"1em"
dvhdvhh-100dvh"100dvh"
svhsvhh-100svh"100svh"
lvhlvhh-100lvh"100lvh"
autoautow-auto"auto"
{/* Move 50% of its own width */}
<div className="animate-hover:x-50pct">Translate by 50%</div>

{/* Full viewport height */}
<div className="animate-enter:h-100vh">Full viewport height</div>

{/* Spacing with rem units */}
<div className="animate-hover:gap-2rem">Gap of 2rem</div>

When no unit suffix is provided, the value is treated as a plain number (pixels for most properties).

Keyframe array syntax

For keyframe animations that cycle through multiple values, use the bracket array syntax:

animate-{gesture}:{property}-[v1,v2,v3]

Values inside the brackets are comma-separated. Each value becomes a keyframe in the animation sequence:

{/* Scale pulses through three keyframes */}
<div className="animate-enter:scale-[100,120,100] animate-repeat-infinite animate-duration-1000">
  Pulsing element
</div>

{/* Opacity fades through keyframes */}
<div className="animate-enter:opacity-[0,100,50,100] animate-duration-2000">
  Multi-step fade
</div>

{/* Rotate through multiple angles */}
<div className="animate-enter:rotate-[0,45,-45,0] animate-repeat-infinite">
  Wiggle animation
</div>

This compiles to Motion's keyframe array syntax:

<motion.div
  animate={{ scale: [1, 1.2, 1] }}
  transition={{ duration: 1, repeat: Infinity }}
/>

You can use the animate-times-[...] class to control the timing of each keyframe (see Transition configuration below).

Transition configuration

Transition classes control how the animation runs. They apply globally to all gestures on the element. These classes have no gesture prefix.

animate-duration-{ms}

Sets the animation duration in milliseconds. The value is converted to seconds internally.

<div className="animate-hover:scale-110 animate-duration-300">300ms duration</div>
<div className="animate-hover:scale-110 animate-duration-1000">1 second duration</div>

animate-delay-{ms}

Sets a delay before the animation starts, in milliseconds.

<div className="animate-enter:opacity-100 animate-delay-200">200ms delay before animating</div>

animate-ease-{type}

Sets the easing function. Available values:

ClassMotion easing value
animate-ease-ineaseIn
animate-ease-outeaseOut
animate-ease-in-outeaseInOut
animate-ease-linearlinear
animate-ease-circ-incircIn
animate-ease-circ-outcircOut
animate-ease-circ-in-outcircInOut
animate-ease-back-inbackIn
animate-ease-back-outbackOut
animate-ease-back-in-outbackInOut
animate-ease-anticipateanticipate
animate-ease-[n,n,n,n][n, n, n, n] (custom cubic-bezier)
animate-ease-steps-{n}"steps(n)"
<div className="animate-enter:y-0 animate-ease-out">Ease out</div>
<div className="animate-enter:y-0 animate-ease-in-out">Ease in and out</div>
<div className="animate-enter:y-0 animate-ease-circ-out">Circular ease out</div>
<div className="animate-enter:y-0 animate-ease-back-out">Back ease out (slight overshoot)</div>
<div className="animate-enter:y-0 animate-ease-anticipate">Anticipate (pull back then spring forward)</div>
<div className="animate-enter:y-0 animate-ease-[0.25,0.1,0.25,1]">Custom cubic-bezier</div>
<div className="animate-enter:y-0 animate-ease-steps-5">Stepped animation (5 steps)</div>

animate-spring

Switches the transition to spring physics instead of duration-based easing. When using animate-spring, the duration and ease values are ignored by Motion in favor of the spring parameters.

<div className="animate-hover:scale-110 animate-spring">Spring animation</div>

animate-stiffness-{n}

Sets the spring stiffness. Higher values create a stiffer, faster spring. Only applies when using animate-spring.

<div className="animate-hover:scale-110 animate-spring animate-stiffness-300">Stiff spring</div>

animate-damping-{n}

Sets the spring damping. Higher values reduce oscillation. Only applies when using animate-spring.

<div className="animate-hover:scale-110 animate-spring animate-damping-20">Moderate damping</div>

animate-mass-{n}

Sets the mass of the animated object. The value is divided by 10 internally (so animate-mass-10 equals mass 1.0). Higher mass means slower, heavier movement.

<div className="animate-hover:scale-110 animate-spring animate-mass-15">Heavier spring (mass 1.5)</div>

animate-bounce-{n}

Sets the spring bounciness as a percentage. The value is divided by 100 internally (so animate-bounce-25 equals 0.25). Only applies when using animate-spring.

<div className="animate-hover:scale-110 animate-spring animate-bounce-30">Bouncy spring</div>

animate-repeat-{n}

Repeats the animation a set number of times.

<div className="animate-enter:rotate-360 animate-repeat-3">Spin 3 times</div>

animate-repeat-infinite

Repeats the animation indefinitely.

<div className="animate-enter:rotate-360 animate-repeat-infinite animate-duration-2000">
  Spin forever, 2 seconds per rotation
</div>

animate-repeat-reverse

Sets the repeat type to "reverse" so the animation alternates direction on each repetition.

<div className="animate-enter:x-100 animate-repeat-3 animate-repeat-reverse animate-duration-1000">
  Slides back and forth 3 times
</div>

animate-repeat-mirror

Sets the repeat type to "mirror" so the animation mirrors on each repetition.

<div className="animate-enter:scale-120 animate-repeat-infinite animate-repeat-mirror">
  Mirrors the scale animation infinitely
</div>

animate-repeat-delay-{ms}

Sets a delay between each repetition, in milliseconds. The value is converted to seconds internally.

<div className="animate-enter:rotate-360 animate-repeat-infinite animate-repeat-delay-500">
  Pauses 500ms between each rotation
</div>

animate-stagger-{ms}

Sets the stagger delay between children animations, in milliseconds. The value is converted to seconds internally. This maps to Motion's staggerChildren transition property.

<div className="animate-stagger-100">
  {/* Each child animates 100ms after the previous one */}
</div>

animate-stagger-reverse

Reverses the stagger direction so the last child animates first. Maps to staggerDirection: -1.

<div className="animate-stagger-100 animate-stagger-reverse">
  {/* Last child animates first */}
</div>

animate-delay-children-{ms}

Delays the start of children animations, in milliseconds. The value is converted to seconds internally.

<div className="animate-delay-children-300 animate-stagger-100">
  {/* Children start animating after 300ms, then stagger by 100ms */}
</div>

animate-when-before

Sets orchestration so the parent animates before its children. Maps to when: "beforeChildren".

<div className="animate-enter:opacity-100 animate-when-before animate-stagger-100">
  {/* Parent fades in first, then children stagger in */}
</div>

animate-when-after

Sets orchestration so the parent animates after its children. Maps to when: "afterChildren".

<div className="animate-enter:opacity-100 animate-when-after">
  {/* Children animate first, then parent fades in */}
</div>

animate-rest-speed-{n}

Sets the rest speed threshold for spring animations. The animation is considered complete when the velocity falls below this value.

<div className="animate-hover:scale-110 animate-spring animate-rest-speed-2">
  Stops spring when velocity drops below 2
</div>

animate-rest-delta-{n}

Sets the rest delta threshold for spring animations. The animation is considered complete when the distance from the target is less than this value.

<div className="animate-hover:x-100 animate-spring animate-rest-delta-1">
  Stops spring when within 1px of target
</div>

animate-times-[...]

Sets the timing for each keyframe in a keyframe animation. Values are comma-separated numbers between 0 and 1 inside brackets.

<div className="animate-enter:opacity-[0,100,50,100] animate-times-[0,0.3,0.7,1] animate-duration-2000">
  Keyframes with custom timing
</div>

Compiles to transition: { times: [0, 0.3, 0.7, 1] }.

Complete spring example

<button className="px-6 py-3 bg-purple-600 text-[var(--color-fg)] rounded-xl animate-hover:scale-110 animate-tap:scale-90 animate-spring animate-stiffness-400 animate-damping-15 animate-bounce-20">
  Springy Button
</button>

This produces:

<motion.button
  className="px-6 py-3 bg-purple-600 text-[var(--color-fg)] rounded-xl"
  whileHover={{ scale: 1.1 }}
  whileTap={{ scale: 0.9 }}
  transition={{ type: "spring", stiffness: 400, damping: 15, bounce: 0.2 }}
>
  Springy Button
</motion.button>

Viewport configuration

These classes configure how scroll-triggered animations behave when using the inview gesture. They have no gesture prefix.

animate-once

The animation runs only the first time the element enters the viewport. After that, it stays in its final state even if the element scrolls out and back in.

<div className="animate-initial:opacity-0 animate-inview:opacity-100 animate-once">
  Fades in once, stays visible
</div>

animate-amount-all

By default, the animation triggers when any part of the element is visible. With animate-amount-all, the entire element must be in the viewport before the animation starts.

<div className="animate-inview:scale-110 animate-amount-all">
  Only animates when fully visible
</div>

animate-amount-{n}

Sets a numeric viewport threshold as a percentage. The value is divided by 100 (so animate-amount-50 equals 0.5, meaning 50% of the element must be visible). This gives more precise control than animate-amount-all.

<div className="animate-inview:opacity-100 animate-amount-50">
  Animates when 50% of the element is visible
</div>
<div className="animate-inview:y-0 animate-amount-75">
  Animates when 75% visible
</div>

Compiles to viewport: { amount: 0.5 } and viewport: { amount: 0.75 } respectively.

animate-margin-{n}

Expands or shrinks the viewport detection area by the specified number of pixels. A positive value triggers the animation before the element is actually in view.

<div className="animate-inview:opacity-100 animate-margin-100">
  Starts animating 100px before entering viewport
</div>

Complete viewport example

<section className="animate-initial:opacity-0 animate-initial:y-30 animate-inview:opacity-100 animate-inview:y-0 animate-duration-700 animate-ease-out animate-once animate-margin-50">
  This section fades and slides up when scrolled into view (with a 50px margin),
  and only animates once.
</section>

Drag configuration

These classes enable and configure drag behavior on the element.

animate-drag-x

Enables dragging along the horizontal axis only.

<div className="w-20 h-20 bg-blue-500 rounded-lg animate-drag-x">
  Drag me left/right
</div>

animate-drag-y

Enables dragging along the vertical axis only.

<div className="w-20 h-20 bg-green-500 rounded-lg animate-drag-y">
  Drag me up/down
</div>

animate-drag-both

Enables dragging in both directions.

<div className="w-20 h-20 bg-red-500 rounded-lg animate-drag-both">
  Drag me anywhere
</div>

animate-drag-elastic-{n}

Sets the elasticity of the drag as a percentage. The value is divided by 100 (so animate-drag-elastic-50 equals 0.5). A value of 0 means no elastic overshoot; 100 means full elasticity.

<div className="w-20 h-20 bg-orange-500 rounded-lg animate-drag-both animate-drag-elastic-30">
  Drag with 30% elasticity
</div>

animate-drag-snap

Snaps the element back to its origin when released. Maps to dragSnapToOrigin.

<div className="w-20 h-20 bg-blue-500 rounded-lg animate-drag-both animate-drag-snap">
  Snaps back on release
</div>

animate-drag-no-momentum

Disables momentum-based movement after the drag ends. Maps to dragMomentum={false}.

<div className="w-20 h-20 bg-blue-500 rounded-lg animate-drag-both animate-drag-no-momentum">
  Stops immediately on release
</div>

animate-drag-lock

Enables direction locking -- once the user starts dragging in one direction, the other axis is locked. Maps to dragDirectionLock.

<div className="w-20 h-20 bg-blue-500 rounded-lg animate-drag-both animate-drag-lock">
  Locks to one axis while dragging
</div>

animate-drag-constraint-t-{n} / animate-drag-constraint-l-{n} / animate-drag-constraint-r-{n} / animate-drag-constraint-b-{n}

Sets drag constraints for each edge in pixels. These map to dragConstraints: { top, left, right, bottom }.

<div className="w-20 h-20 bg-blue-500 rounded-lg animate-drag-both animate-drag-constraint-t--100 animate-drag-constraint-b-100 animate-drag-constraint-l--100 animate-drag-constraint-r-100">
  Drag within a 200x200 area
</div>

Combining drag with gestures

Drag classes can be combined with the drag gesture prefix to style the element while it is being dragged:

<div className="w-20 h-20 bg-blue-500 rounded-lg animate-drag-both animate-drag:scale-110 animate-drag:rotate-10 animate-spring">
  Grows and tilts while dragging
</div>

Layout configuration

These classes enable and configure Motion's layout animations. Layout animations automatically animate elements when their position or size changes in the DOM.

animate-layout

Enables layout animation on the element. Maps to layout.

<div className="animate-layout">
  Automatically animates when layout changes
</div>

animate-layout-position

Only animates position changes, not size. Maps to layout="position".

<div className="animate-layout-position">
  Only animates position changes
</div>

animate-layout-size

Only animates size changes, not position. Maps to layout="size".

<div className="animate-layout-size">
  Only animates size changes
</div>

animate-layout-preserve

Animates layout changes while preserving the element's aspect ratio. Maps to layout="preserve-aspect".

<div className="animate-layout-preserve">
  Preserves aspect ratio during layout animation
</div>

animate-layout-id-{name}

Assigns a layout ID for shared layout animations. Elements with the same layoutId animate between each other when one mounts and the other unmounts. Maps to layoutId="{name}".

<div className="animate-layout-id-hero">
  Shared layout animation with other "hero" elements
</div>

animate-layout-scroll

Compensates for scroll offset during layout animations. Maps to layoutScroll.

<div className="animate-layout-scroll">
  Accounts for scroll position in layout animation
</div>

animate-layout-root

Marks the element as a layout animation boundary. Maps to layoutRoot.

<div className="animate-layout-root">
  Layout animations are scoped to this subtree
</div>

Combining everything

Here is a full example that uses gestures, transitions, viewport config, and multiple properties:

<div className="p-8 bg-white rounded-2xl shadow-lg animate-initial:opacity-0 animate-initial:y-40 animate-initial:scale-90 animate-inview:opacity-100 animate-inview:y-0 animate-inview:scale-100 animate-hover:scale-105 animate-hover:rotate-1 animate-tap:scale-95 animate-duration-600 animate-ease-out animate-once">
  <h2 className="text-2xl font-bold">Card Title</h2>
  <p className="mt-2 text-gray-600">
    This card fades, slides, and scales in when scrolled into view.
    It also responds to hover and tap.
  </p>
</div>

Property quick reference

Class patternMotion outputValue type
scale-{n}scale: n/100Percentage (110 = 1.1)
scale-x-{n}scaleX: n/100Percentage
scale-y-{n}scaleY: n/100Percentage
scale-z-{n}scaleZ: n/100Percentage
rotate-{n}rotate: nDegrees
rotate-x-{n}rotateX: nDegrees
rotate-y-{n}rotateY: nDegrees
skew-{n}skew: nDegrees (uniform)
skew-x-{n}skewX: nDegrees
skew-y-{n}skewY: nDegrees
origin-x-{n}originX: n/100Percentage (0-100 → 0-1)
origin-y-{n}originY: n/100Percentage (0-100 → 0-1)
origin-z-{n}originZ: nPixels
perspective-{n}perspective: nPixels
x-{n}x: nPixels
y-{n}y: nPixels
z-{n}z: nPixels
opacity-{n}opacity: n/100Percentage (0-100)
blur-{n}filter: "blur(Npx)"Pixels
backdrop-blur-{n}backdropFilter: "blur(Npx)"Pixels
brightness-{n}filter: "brightness(n/100)"Percentage
contrast-{n}filter: "contrast(n/100)"Percentage
saturate-{n}filter: "saturate(n/100)"Percentage
clip-[value]clipPath: "value"String
bg-{color}backgroundColor: colorColor (#hex/rgb/hsl)
text-{color}color: colorColor (#hex/rgb/hsl)
border-{color}borderColor: colorColor (#hex/rgb/hsl)
shadow-[value]boxShadow: "value"String
w-{n}width: nPixels
h-{n}height: nPixels
rounded-{n}borderRadius: nPixels
top-{n}top: nPixels (+ units)
left-{n}left: nPixels (+ units)
right-{n}right: nPixels (+ units)
bottom-{n}bottom: nPixels (+ units)
p-{n}padding: nPixels (+ units)
m-{n}margin: nPixels (+ units)
gap-{n}gap: nPixels (+ units)
text-size-{n}fontSize: nPixels (+ units)
tracking-{n}letterSpacing: nPixels (+ units)
leading-{n}lineHeight: nPixels (+ units)
border-w-{n}borderWidth: nPixels
path-length-{n}pathLength: nNumber
path-offset-{n}pathOffset: nNumber
path-spacing-{n}pathSpacing: nNumber
{prop}-[v1,v2,v3]prop: [v1, v2, v3]Keyframe array
[key=value]key: valueAny