Tilt Card
A gentle shift in perspective that makes a flat card feel like an object.
ANTICRAFTDIGITAL OBJECTS
COLLECTION 001
COLLECTION 001
A different
perspective.
perspective.
Live component. Go ahead, interact.
REACT / TYPESCRIPT
'use client'
import type { CSSProperties, HTMLAttributes, PointerEvent } from 'react'
import './tilt-card.css'
export interface TiltCardProps extends HTMLAttributes<HTMLDivElement> {
/** Maximum rotation in degrees, from 0 to 30. */
maxRotation?: number
/** Distance to the viewer in pixels. */
perspective?: number
}
export function TiltCard({
children,
className = '',
maxRotation = 8,
perspective = 900,
style,
onPointerMove,
onPointerLeave,
onPointerCancel,
...props
}: TiltCardProps) {
const rotation = Number.isFinite(maxRotation) ? Math.max(0, Math.min(30, maxRotation)) : 8
const depth = Number.isFinite(perspective) ? Math.max(100, perspective) : 900
function resetTilt(element: HTMLDivElement) {
element.style.removeProperty('--ac-tilt-x')
element.style.removeProperty('--ac-tilt-y')
}
function tilt(event: PointerEvent<HTMLDivElement>) {
onPointerMove?.(event)
if (
event.defaultPrevented ||
event.pointerType !== 'mouse' ||
window.matchMedia('(prefers-reduced-motion: reduce), (pointer: coarse)').matches
) return
const bounds = event.currentTarget.getBoundingClientRect()
if (!bounds.width || !bounds.height) return
const x = Math.max(-1, Math.min(1, ((event.clientX - bounds.left) / bounds.width - 0.5) * 2))
const y = Math.max(-1, Math.min(1, ((event.clientY - bounds.top) / bounds.height - 0.5) * 2))
event.currentTarget.style.setProperty('--ac-tilt-x', `${-y * rotation}deg`)
event.currentTarget.style.setProperty('--ac-tilt-y', `${x * rotation}deg`)
}
return (
<div
{...props}
className={`ac-tilt-card ${className}`.trim()}
style={{ '--ac-tilt-perspective': `${depth}px`, ...style } as CSSProperties}
onPointerMove={tilt}
onPointerLeave={(event) => {
onPointerLeave?.(event)
resetTilt(event.currentTarget)
}}
onPointerCancel={(event) => {
onPointerCancel?.(event)
resetTilt(event.currentTarget)
}}
>
{children}
</div>
)
}
01
Installation
Run this in your shadcn-configured React project. The component and stylesheet are added together.
TERMINAL
npx shadcn@latest add http://localhost:5173/r/tilt-card.jsonThis command uses the current site address. For localhost, keep this preview running on the same computer. Files follow your configured components alias.
02
Usage
Import the component and make the details your own.
TypeScript
import { TiltCard } from '@/components/anticraft/TiltCard';
export function Example() {
return (
<TiltCard maxRotation={8} perspective={900} style={{ padding: '2rem' }}>
<h3>A different perspective.</h3>
<p>Small details. A deeper impression.</p>
</TiltCard>
);
}03
Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | The contents of the dimensional card. |
maxRotation | number | 8 | Maximum rotation in degrees, clamped between 0 and 30. |
perspective | number | 900 | Viewer distance in pixels, with a minimum of 100. |
className | string | '' | Additional classes for the card. |
style | CSSProperties | — | Inline styles and CSS custom properties. Native div attributes are supported. |
04
Motion & accessibility
Considered by default.
Tilts only with mouse input and returns to center when the pointer leaves. Reduced motion, coarse pointers, and devices without hover keep the card flat.
Use semantic content and meaningful labels in your composition. Check contrast and keyboard behavior with your own styles and content.