Tilt Card
A 3D perspective tilt card — the card tilts with the cursor, a glare sweeps across its surface, it springs back on leave, and content can float up with translateZ.
Loading preview…
npx shadcn@latest add https://webberui.com/r/tilt-card.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
12
1.02
<TiltCard />
Installation
npx shadcn@latest add https://webberui.com/r/tilt-card.jsonInstalling also writes the --wb-duration-fast CSS variable into your global stylesheet.
Usage
import { TiltCard, TiltCardItem } from "@/components/ui/tilt-card";
<TiltCard maxTilt={12} scale={1.02}>
<div className="h-40 rounded-t-xl bg-neutral-100 dark:bg-neutral-800" />
<TiltCardItem z={40} className="p-5">
<h3 className="font-semibold">Title</h3>
<p className="text-sm text-neutral-500">This content floats 40px above the card face.</p>
</TiltCardItem>
</TiltCard>Props
TiltCard
| Prop | Type | Default | Description |
|---|---|---|---|
maxTilt | number | 12 | Maximum tilt angle (degrees); rotateX / rotateY reach this angle when the cursor is at the card's edge |
glare | boolean | true | Whether to show the glare layer that follows the cursor |
scale | number | 1.02 | Scale factor on hover; 1 means no scaling |
springConfig | SpringOptions | { stiffness: 300, damping: 25, mass: 0.5 } | Spring parameters shared by the tilt, scale, and spring back |
className | string | — | Styles for the card body (a neutral rounded card by default, overridable; do not add overflow-hidden) |
children | ReactNode | — | Card content |
Remaining <div> attributes (onClick, aria-*, and so on) are forwarded to the outer perspective container.
TiltCardItem
| Prop | Type | Default | Description |
|---|---|---|---|
z | number | 40 | Height the content floats toward the viewer along the Z axis (px) |
className | string | — | Custom styles |
children | ReactNode | — | The content to float |
How it works
- 3D perspective: the outer container is fixed at
perspective: 1000pxand the card body usestransform-style: preserve-3d; rotateX / rotateY map linearly to ±maxTiltbased on the cursor's position relative to the center - Glare: a white radial-gradient highlight follows the cursor position, growing stronger the further the cursor is from the center (0 when dead-center, brighter the more the card tilts); the highlight color can be overridden with the
--wb-tilt-glareCSS variable (defaultrgba(255, 255, 255, 0.4)) - Spring back: every transform is driven by
useMotionValue+useSpring, so once the cursor leaves the card springs back with momentum and the glare fades out - Floating layers:
TiltCardItemusestranslateZto lift content off the card face; it must be a direct child ofTiltCard, and if another container sits in between, give that containertransform-style: preserve-3d - Avoid overflow-hidden: setting
overflow-hiddenon the card body flattens the 3D context (translateZstops working), so for full-bleed images add the radius to the image itself instead (for examplerounded-t-xl) - Stable, no jitter: pointer events are attached to the untransformed outer container, so the card never reads back its own transformed bounds mid-rotation and feeds jitter into the loop
- Automatically disabled on touch devices: only mouse pointer events are handled (
pointerType === "mouse"), so on touch it is just an ordinary card - When the user has "reduce motion" enabled at the system level, the tilt and glare are disabled; the card stays static and its content and functionality are unaffected