4 Pure CSS3 with JSPublished 2026-10-05Free · no sign-up
Seven ways to give a card depth: a tilt that follows the pointer, a script-free tilt from hover zones, a moving glare, layers at different depths, a payment card with a sheen, a tilt that also runs on keyboard focus and a card that floats above its own shadow.
The card rotates on two axes to face the pointer, with perspective on the parent. Script converts the pointer position into two angles of at most 10 degrees, stored in custom properties, and the card eases back flat when the pointer leaves. It does nothing for reduced motion.
View code
<div class="tc1"><article class="card"><p class="k">New</p><h3>Aurora headphones</h3><p>40 hours of battery, folding frame.</p></article></div>
var c = document.querySelector('.tc1 .card');
if (!matchMedia('(prefers-reduced-motion: reduce)').matches) {
c.addEventListener('pointermove', function (e) {
var r = c.getBoundingClientRect(), x = (e.clientX - r.left) / r.width - .5, y = (e.clientY - r.top) / r.height - .5;
c.classList.add('live');
c.style.setProperty('--ry', x * 20 + 'deg'); c.style.setProperty('--rx', -y * 20 + 'deg');
});
c.addEventListener('pointerleave', function () { c.classList.remove('live'); c.style.setProperty('--rx', '0deg'); c.style.setProperty('--ry', '0deg'); });
}
02. CSS-Only Tilt with Hover Zones
3DNo JS
No script: nine invisible zones cover the card in a grid, and hovering a zone tilts the card toward it with :has(). It is a useful trick when you cannot add JavaScript. The zones are empty elements hidden from assistive technology.
View code
<div class="tc2"><div class="z" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div><article><h3>Hover the corners</h3><p>The card leans toward the zone under the pointer.</p></article></div>
On top of the tilt, a soft white glare moves to the side nearest the pointer, as if light were catching a glossy surface. The glare is a radial gradient on a pseudo-element positioned by the same custom properties the tilt uses.
View code
<div class="tc3"><article class="card"><h3>Night pass</h3><p>Valid 18:00 to 06:00</p></article></div>
var c = document.querySelector('.tc3 .card'), still = matchMedia('(prefers-reduced-motion: reduce)').matches;
c.addEventListener('pointermove', function (e) {
var r = c.getBoundingClientRect(), x = (e.clientX - r.left) / r.width, y = (e.clientY - r.top) / r.height;
c.style.setProperty('--gx', x * 100 + '%'); c.style.setProperty('--gy', y * 100 + '%');
if (!still) { c.style.setProperty('--ry', (x - .5) * 16 + 'deg'); c.style.setProperty('--rx', (.5 - y) * 16 + 'deg'); }
});
c.addEventListener('pointerleave', function () { c.style.setProperty('--rx', '0deg'); c.style.setProperty('--ry', '0deg'); });
04. Layered Depth on Hover
3DParallax
Elements inside the card sit at different depths with translateZ inside a preserve-3d parent, so when the card tilts on hover the badge and title move more than the background. It gives a pop-up-book feeling with a single fixed rotation and no script.
View code
<div class="tc4"><article class="card"><span class="badge">-30%</span><h3>Linen shirt</h3><p>Summer sale ends Sunday</p></article></div>
A credit-card-sized mock-up that tilts toward the pointer and shows a diagonal sheen. The number is fake and shortened to the last four digits, which is also what you should show on a real account page. aspect-ratio keeps the 85.6 by 54 mm card proportions at any width.
var c = document.querySelector('.tc5 .cc');
if (!matchMedia('(prefers-reduced-motion: reduce)').matches) {
c.addEventListener('pointermove', function (e) {
var r = c.getBoundingClientRect();
c.style.setProperty('--ry', ((e.clientX - r.left) / r.width - .5) * 18 + 'deg');
c.style.setProperty('--rx', (.5 - (e.clientY - r.top) / r.height) * 18 + 'deg');
});
c.addEventListener('pointerleave', function () { c.style.setProperty('--rx', '0deg'); c.style.setProperty('--ry', '0deg'); });
}
06. Tilt on Keyboard Focus Too
3DAccessibility
Three linked cards that tilt slightly when hovered or focused, so keyboard users get the same feedback as mouse users. The tilt is a fixed CSS rotation on :hover and :focus-visible, which needs no script and is easy to switch off for reduced motion.
The card floats slowly up and down while a separate shadow ellipse beneath it shrinks and softens in step, which sells the height far better than a box-shadow alone. Both use the same duration and easing so they stay in sync.
View code
<div class="tc7"><article><h3>Pro plan</h3><p>Everything in Team, plus audit logs.</p></article><span class="sh" aria-hidden="true"></span></div>
3D rotation looks flat until something sets a viewing distance. Put perspective: 700px on the card's parent; smaller values exaggerate the effect, larger ones flatten it.
Keep tilt small
Ten to fifteen degrees reads as depth; more makes text hard to read and can cause motion sickness. Every demo checks prefers-reduced-motion and stays flat when it is set.
Depth inside the card
Give the card transform-style: preserve-3d and move children forward with translateZ(). When the card turns, nearer layers shift further, giving a parallax effect with no extra script.
Script only for following the pointer
A fixed tilt on hover needs only CSS. To follow the pointer, a few lines of JavaScript turn the pointer position into two angles and store them in custom properties that the CSS transform reads.
Frequently asked questions
Do tilt cards work on phones?
Phones have no hover, so the cards simply stay flat. That is the right default: the content is the same.
Why does text look blurry while tilted?
Rotated text is resampled. Keep angles small and the effect brief, and the card snaps back crisp when the pointer leaves.
Can I use a library instead?
You can, but the demos show the whole effect fits in a few lines of CSS and script.