Studio note
Depth that follows your pointer
A restrained perspective and moving highlight give this surface presence while its content and action remain clear and stable.
A free card component with spring-smoothed 3D pointer tilt.
Tilt Card is a free card component for Next.js and React, styled with Tailwind, that follows a fine pointer with restrained 3D perspective and a moving glare highlight. Motion springs smooth the interaction and return the surface to its complete, flat state when the pointer leaves.
Touch devices and people who prefer reduced motion receive the same readable static card without tilt or glare. Pointer movement updates Motion values directly instead of React state, so the decorative interaction does not cause a component render on every move.
Studio note
A restrained perspective and moving highlight give this surface presence while its content and action remain clear and stable.
Add the @te registry to your components.json once:
{
"registries": {
"@te": "https://templateempire.io/r/{name}.json"
}
}Then install the component:
npx shadcn@latest add @te/card-tiltimport { TiltCard } from "@/components/ui/card-tilt";
export default function Page() {
return (
<TiltCard maxTilt={12} glare>
<article className="space-y-3 p-6">
<h2 className="text-xl font-semibold">A card with depth</h2>
<p className="text-muted">Your content stays clear at every angle.</p>
</article>
</TiltCard>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | required | Card content, including any interactive children. |
| maxTilt | number | 10 | Maximum rotation in degrees on each axis, clamped from 2 to 20. |
| glare | boolean | true | Whether to show the decorative moving glare highlight. |
| className | string | undefined | Additional classes merged onto the card root. |
Set the card's Tier 3 glare property to another semantic colour token. The default falls back to the foreground token, so a fresh shadcn installation does not need Template Empire brand tokens.
:root {
--card-tilt-glare: var(--primary);
}