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.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add tilt-card.enTilt Card is a React 3D tilt card component built with Motion and Tailwind CSS. It uses CSS perspective with rotateX/rotateY for depth, and TiltCardItem lets a title or button float above the card face.
Playground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<TiltCard/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
When to use
- Product or project cards in a store or portfolio
- Plan cards on a pricing page, so the hovered plan stands out
- Showcases that should feel physical, such as membership cards, tickets, or badges
- Character or prize cards on gaming and event pages
- Not a good fit: pages used mostly on phones, since it doesn't tilt on touch. For a rainbow foil collectible-card look, see Holographic Card
Installation
npx webberui@latest add https://webberui.com/r/tilt-card.en.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>Examples
Product card with Next.js Image
The card body must not use overflow-hidden, so round the corners on the full-bleed image itself.
import Image from "next/image";
import { TiltCard, TiltCardItem } from "@/components/ui/tilt-card";
<TiltCard className="w-72">
<Image
src="/products/headphones.jpg"
alt="Wireless noise-cancelling headphones"
width={288}
height={192}
className="h-48 w-full rounded-t-xl object-cover"
/>
<TiltCardItem z={30} className="space-y-1 p-5">
<h3 className="font-semibold">Wireless noise-cancelling headphones</h3>
<p className="text-sm text-neutral-500">30-hour battery, multipoint pairing</p>
</TiltCardItem>
</TiltCard>Custom glare colour
The glare reads the --wb-tilt-glare CSS variable, which you can set on TiltCard through style.
<TiltCard
className="w-64 bg-neutral-950 p-6 text-white"
style={{ "--wb-tilt-glare": "rgba(56, 189, 248, 0.35)" } as React.CSSProperties}
>
<TiltCardItem z={50}>
<p className="text-xs uppercase tracking-widest text-sky-300">Member</p>
<p className="mt-2 text-2xl font-semibold">Annual membership</p>
</TiltCardItem>
</TiltCard>Subtle plan cards
For pricing cards side by side, lower maxTilt, set scale to 1, and turn off glare for a more restrained effect.
<div className="grid gap-6 sm:grid-cols-3">
{plans.map((plan) => (
<TiltCard key={plan.id} maxTilt={6} scale={1} glare={false} className="p-6">
<h3 className="font-semibold">{plan.name}</h3>
<p className="mt-2 text-sm text-neutral-500">{plan.summary}</p>
</TiltCard>
))}
</div>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
FAQ
Does it work with the Next.js App Router?
Yes. The file already starts with "use client", so you can import it from a Server Component page as usual.
Why isn't TiltCardItem floating?
TiltCardItem needs to be a direct child of TiltCard. If there's a wrapper in between, give that wrapper transform-style: preserve-3d (in Tailwind, [transform-style:preserve-3d]). Adding overflow-hidden to the card body also flattens the 3D.
Does it work on phones?
No. The component only responds to pointer events whose pointerType is mouse; on touch devices it renders as a regular card, and its content and clicks are unaffected.
How do I tune the tilt and feel?
maxTilt sets the maximum angle when the cursor reaches the edge, scale sets the hover zoom, and springConfig takes Motion's SpringOptions to adjust how fast and springy the return is.
What are the dependencies?
motion, clsx, and tailwind-merge. If you'd rather skip the CLI, the Manual tab in the installation section walks you through copying the source.
Glass Tab Bar
A frosted-glass mobile bottom tab bar React component. Icons pop in one by one on mount, the active pill slides between tabs and the active label expands, with an optional raised centre button and unread badges.
Hover Expand Gallery
A horizontally squeezing expand gallery — panels change their share of the space on hover or keyboard focus, while the rest collapse and dim.