Tilt
Tilt a surface toward the pointer, with optional glare, then return it to flat.
import { Tilt } from '@youj/motion'
Installation & usage
npm i @youj/motionFor an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.
Use it on one showcase surface that invites a closer look: a ticket, a pricing card, a product shot. Keep it off grids of everyday cards, text-heavy panels, where tilted type is harder to read, and anything people click with precision. For a lighter touch on many cards, use a Spotlight.
Wrap the surface, and put the rounded corners on Tilt itself. Its inner layer inherits them, so the glare clips to the same shape.
import { Tilt } from '@youj/motion'
export function Ticket() {
return (
<Tilt glare scale={1.02} className="rounded-2xl">
<div className="rounded-[inherit] border bg-surface p-5">…</div>
</Tilt>
)
}Description
A ticket-style event card that tilts toward the pointer, with glare and a slight scale.
Templates with different rotation, glare and scale settings.
max is the rotation, in degrees, at the edges: the pointer's position across the surface maps linearly from -max to max on both axes. perspective is the CSS perspective distance, where lower is more dramatic. scale grows the surface while it's hovered, and glare adds a soft highlight that follows the pointer.
The glare is a pre-drawn gradient, twice the surface's size, that translates under a soft-light blend. It never repaints, so it stays on the compositor. Its color follows the theme.
A card whose icon and text sit at different depths in front of the surface.
Tilt's inner layer preserves 3D. Keep transform-style: preserve-3d on the way down, and avoid overflow: hidden in between, which flattens it. Then children can sit in front of the surface with translate-z-*, and they shift against it as it turns.
Code
tilt-demo.tsx, the Demo story. The component itself comes from the package.
Reference
Propsmax, perspective, glare… (6)
Props
| Prop | Type | |
|---|---|---|
max | numberMaximum rotation in degrees at the edges. default 8 | 8 |
perspective | numberCSS perspective distance in px. default 900 | 900 |
glare | booleanA soft highlight that tracks the pointer. default false | false |
scale | numberScale while hovered. default 1 | 1 |
spring | SpringToken'snappy''smooth''settle'default 'smooth' | 'smooth' |
children | ReactNode | – |
Other props pass through: extends HTMLAttributes<HTMLDivElement>. | ||
MotionTilt, hover scale and glare, return · reduced motion
Motion
| Moment | Tokens | ||
|---|---|---|---|
TiltrotateX and rotateY, up to max degrees at the edges | spring.smooth0.36s, bounce 0 | 0.36s, bounce 0 | |
| Hover scale and glaregrow and fade in together | spring.smooth0.36s, bounce 0 | 0.36s, bounce 0 | |
| Returnsprings flat when the pointer leaves | spring.smooth0.36s, bounce 0 | 0.36s, bounce 0 |
Reduced motion
Flat and still: no rotation, no scale and no glare.
spring swaps the default for another spring token. Every value is a transform or an opacity, so a tilt never triggers layout or paint.
Reduced motion
The surface stays flat and still. There's no rotation, no hover scale and no glare, and the content reads exactly as it does at rest.
AccessibilityKeyboard, ARIA, focus
Accessibility
- Tilt adds no roles and no focus stop. The content inside keeps its semantics, and interactive children keep their focus rings.
- It responds to a mouse only. Touch and pen input scroll and tap as usual, and keyboard focus doesn't tilt the surface.
- The glare layer is
aria-hiddenand ignores the pointer. - Keep
maxsmall on anything with text. Past about 10 degrees, type foreshortens enough to slow reading.