Magnetic button
CSS + JSInside its field the button shifts and leans toward the pointer while its label floats higher, so the two slide apart. JS only reports the pointer position; release it and a springy transition takes it home.
Pointer-following tilt without JavaScript: nine invisible hover zones steer the card.
Your edited version
zone:hover ~ .card can set a tilt toward that zone.pointer-events: none on the card lets the pointer fall through to the zones. More zones = smoother tilt; the JS version is smoother still.--u, tied to the canvas, so it is the same share of a gallery card, the editor and a recording canvas.3 × 3 grid of hover zones:hover ~ sibling selectorpointer-events: none on the card<div class="zones" tabindex="0" role="img" aria-label="Card that tilts toward the zone the pointer is in">
<i></i><i></i><i></i>
<i></i><i></i><i></i>
<i></i><i></i><i></i>
<div class="card">Hover around me</div>
</div>
.zones {
/* one base unit: every length below is a multiple of it, so the card is the same share of a
card, the editor, a full screen and a recording canvas */
--u: 0.32vmin;
position: relative;
display: grid;
grid-template: repeat(3, 1fr) / repeat(3, 1fr);
/* the zones are the whole canvas, so the tilt follows the pointer anywhere on it and no hit
area ever hangs past the canvas edge */
width: 100%;
height: 100%;
perspective: calc(800 * var(--u));
}
.zones i { z-index: 1; } /* invisible hover targets, above the card */
.card {
position: absolute;
inset: 0;
display: grid;
place-items: center;
width: calc(260 * var(--u));
height: calc(160 * var(--u));
margin: auto;
border-radius: calc(18 * var(--u));
color: #fff;
font: 800 calc(17.6 * var(--u)) Inter, system-ui;
/* deep enough for white words, 5.2:1 at its worst end: the bright #2ee6d6 was 2.2:1 */
background: linear-gradient(135deg, #087a70, #6a45f5);
box-shadow: 0 calc(22 * var(--u)) calc(36 * var(--u)) calc(-16 * var(--u)) #8b6cff;
pointer-events: none;
transition: transform 0.35s ease-out;
}
/* row 1 tilts back, row 3 tilts forward; column 1 turns left, column 3 turns right */
.zones i:nth-child(1):hover ~ .card { transform: rotateX(22deg) rotateY(-22deg); }
.zones i:nth-child(2):hover ~ .card { transform: rotateX(22deg); }
.zones i:nth-child(3):hover ~ .card { transform: rotateX(22deg) rotateY(22deg); }
.zones i:nth-child(4):hover ~ .card { transform: rotateY(-22deg); }
.zones i:nth-child(6):hover ~ .card { transform: rotateY(22deg); }
.zones i:nth-child(7):hover ~ .card { transform: rotateX(-22deg) rotateY(-22deg); }
.zones i:nth-child(8):hover ~ .card { transform: rotateX(-22deg); }
.zones i:nth-child(9):hover ~ .card { transform: rotateX(-22deg) rotateY(22deg); }
/* the keyboard: one stop for the whole, and the card tilts as it does for the top right zone, even
with the pointer resting in another zone */
.zones:focus-visible .card { transform: rotateX(22deg) rotateY(22deg) !important; }