tiltHover
Scales up into rest on entry, then tips a few degrees off square and swells past full size while hovered, and compresses to the press scale on tap. Both interactive states run on their own press-length transition, so the feedback lands under the pointer rather than at entrance speed. The entrance itself is the same scale-up zoomIn plays; the tilt and the press are what this preset adds.
tiltHover in Motion
Scales up into rest on entry, then tips a few degrees off square and swells past full size while hovered, and compresses to the press scale on tap. Both interactive states run on their own press-length transition, so the feedback lands under the pointer rather than at entrance speed. The entrance itself is the same scale-up zoomIn plays; the tilt and the press are what this preset adds.
Usage
import { Section } from "@/lib/motion";
import { tiltHover } from "@/lib/motion/presets/tilt-hover";
export function Features() {
return (
<Section {...tiltHover} className="grid gap-6 md:grid-cols-3">
<FeatureCard title="Copy, do not install" />
<FeatureCard title="One file you own" />
<FeatureCard title="Presets you spread" />
</Section>
);
}Installation
npx shadcn@latest add https://motion.asmitsah.dev/r/tilt-hover.jsonProps and tokens
What it sets
Transition and viewport
Every motion prop the preset does not set — whileHover, whileTap, whileFocus — stays available at the call site. Write it after the spread and it wins.
layout and drag need the domMax feature bundle, not domAnimation — swap the lazy import in provider.tsx. It adds roughly 13kb gzipped on top of domAnimation.