Rotation
Rotate elements in 2D with degree values.
Rotation spins elements around a pivot point. Use rotate for standard 2D rotation, or rotateX / rotateY for 3D perspective tilts. All values are in degrees — no unit suffix needed.
Basic Rotation
Pass rotate inside from or to. Positive values spin clockwise, negative values spin counter-clockwise.
import { Motion } from "@motion.page/sdk";
// Spin in from 90° clockwise
Motion("spin-in", ".icon", {
from: { rotate: 90, opacity: 0 },
duration: 0.5,
ease: "power3.out",
}).onPageLoad(); Because rotate: 0 is a natural CSS default, omitting to is correct here — the SDK resolves the missing endpoint automatically and animates the element back to its un-rotated state.
Direction
| Value | Direction |
|---|---|
Positive (rotate: 45) | Clockwise |
Negative (rotate: -45) | Counter-clockwise |
// Counter-clockwise entrance
Motion("spin-ccw", ".arrow", {
from: { rotate: -90, opacity: 0 },
duration: 0.4,
ease: "expo.out",
}).onPageLoad(); Full Rotations
Use multiples of 360 to spin the element one or more complete turns.
// One full clockwise spin
Motion("spin-once", ".logo", {
from: { rotate: 0 },
to: { rotate: 360 },
duration: 1,
ease: "power2.inOut",
}).onClick({ each: true }); A value of 360 is one full turn, 720 is two full turns, -360 is one full counter-clockwise turn, and so on.
Transform Origin
Rotation always pivots around the transform origin — by default the element’s center (50% 50%). Change this with transformOrigin to spin from a corner, edge, or any custom point.
| Value | Pivot point |
|---|---|
'center' | Center of the element (default) |
'top left' | Top-left corner (0% 0%) |
'bottom right' | Bottom-right corner (100% 100%) |
'0% 100%' | Bottom-left corner |
'50% 0%' | Top center |
// Spin from the bottom-left corner (like a clock hand)
Motion("clock-hand", ".needle", {
from: { rotate: -30, transformOrigin: "0% 100%" },
to: { rotate: 30 },
duration: 0.8,
ease: "power2.inOut",
}).onPageLoad(); See the Transform Origin page for all supported values and examples.
Combining with Other Transforms
Rotation pairs naturally with scale and opacity to create richer entrance effects.
// Scale + rotate for a spiral entrance
Motion("spiral-in", ".card", {
from: { scale: 0, rotate: 180, opacity: 0 },
duration: 0.6,
ease: "back.out(1.4)",
}).onPageLoad(); // Fade + rotate for a spin-in reveal
Motion("spin-fade", ".badge", {
from: { rotate: -45, opacity: 0 },
duration: 0.4,
ease: "power3.out",
stagger: 0.1,
}).onScroll({ scrub: false, toggleActions: "play none none none" }); Common Patterns
Spinner
An infinitely rotating element using repeat: -1 and a linear ease for constant speed.
Motion("spinner", ".loader", {
from: { rotate: 0 },
to: { rotate: 360 },
duration: 1,
ease: "none",
repeat: -1,
}).onPageLoad(); Wiggle
Alternate between small positive and negative angles with yoyo: true for an attention-grabbing shake.
Motion("wiggle", ".notification", {
to: { rotate: 12 },
duration: 0.12,
ease: "power1.inOut",
repeat: { times: 5, yoyo: true },
}).onPageLoad(); yoyo: true reverses the animation on each cycle, rocking the element back and forth without any hard reset.
Spiral In
Combine scale: 0 with rotate: 180 so the element grows and unwinds into its natural state simultaneously.
Motion("spiral-in", ".icon", {
from: { scale: 0, rotate: 180, opacity: 0 },
duration: 0.7,
ease: "expo.out",
}).onPageLoad(); Because rotate: 0, scale: 1, and opacity: 1 are all natural CSS defaults, omitting to is correct — the SDK fills in the endpoints automatically.
Hover Spin
Rotate an icon a quarter-turn on hover and reverse on leave.
Motion("hover-spin", ".arrow-icon", {
to: { rotate: 90 },
duration: 0.3,
ease: "power2.out",
}).onHover({ each: true, onLeave: "reverse" }); Scroll-Driven Rotation
Drive rotation with scroll progress for a parallax feel.
Motion("scroll-rotate", ".decorative-ring", {
from: { rotate: 0 },
to: { rotate: 360 },
duration: 1,
}).onScroll({ scrub: true }); Notes
- Values are in degrees.
rotate: 90means 90 degrees — no unit suffix. rotate: 0is the natural CSS default. Do not includeto: { rotate: 0 }when animating from a rotated state back to normal — the SDK resolves it automatically.- For 3D perspective tilts, use
rotateX(tilt toward/away) androtateY(tilt left/right). These require aperspectiveset on the parent element in CSS. - Combining
repeat: -1withease: "none"(orease: "linear") produces the smoothest continuous spinner — eased repeats will stutter at the loop boundary. - Large rotation values on text or images can cause subpixel rendering artifacts. Prefer
will-change: transformon elements that animate frequently.