Solar system
Pure CSSOrbits are rings on a tilted plane, each spinning at its own speed. Every planet undoes both rotations, so it always faces the camera.
Sweep across the floor. Tiles rise instantly and sink slowly, leaving a trail.
Your edited version
:hover works on the tile you actually see under the pointer, even on a tilted plane.::before plate lifts. If the hovered element moved, it would slide out from under the pointer and flicker.:hover rule overrides transition-duration to 0.08s — that one applies when the hover starts.hit-testing works in 3Dfast transition in, slow transition outisometric floor<div class="scene">
<div class="floor">
<i></i><i></i><i></i><i></i><i></i>
<i></i><i></i><i></i><i></i><i></i>
<i></i><i></i><i></i><i></i><i></i>
<i></i><i></i><i></i><i></i><i></i>
<i></i><i></i><i></i><i></i><i></i>
</div>
</div>
.scene {
perspective: 900px;
}
.floor {
display: grid;
grid-template-columns: repeat(5, 46px);
gap: 5px;
transform-style: preserve-3d;
transform: rotateX(56deg) rotateZ(-45deg);
/* same plane as its cells: keep the floor itself out of hit-testing, or hover misses in patches */
pointer-events: none;
}
/* the cell is a fixed hit target; only its ::before plate moves */
.floor i {
pointer-events: auto;
position: relative;
height: 46px;
transform-style: preserve-3d;
}
.floor i::before {
content: '';
position: absolute;
inset: 0;
border-radius: 7px;
background: rgb(139 108 255 / 0.35);
border: 1px solid rgb(139 108 255 / 0.7);
pointer-events: none;
/* slow on the way down */
transition: transform 1.4s ease-out, background 1.4s ease-out;
}
.floor i:hover::before {
background: #2ee6d6;
transform: translateZ(46px);
/* instant on the way up */
transition-duration: 0.08s;
}
.d-isotiles {
display: grid;
grid-template-columns: repeat(5, 34px);
gap: 4px;
transform-style: preserve-3d;
transform: rotateX(56deg) rotateZ(-45deg);
// The container lies on exactly the same 3D plane as its children. Coplanar surfaces have no
// stable front-to-back order, so in patches the browser hit-tests the (invisible) container
// instead of the child: wrong cursor, ignored hover. Only the children should catch the pointer.
pointer-events: none;
// The cell never moves: it is the hit target. If the hovered element itself lifted, it would
// slide out from under the pointer, lose :hover, drop, regain it... and flicker.
i {
position: relative;
height: 34px;
pointer-events: auto;
transform-style: preserve-3d;
// the visible plate
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: 6px;
background: color-mix(in srgb, var(--accent) 35%, transparent);
border: 1px solid color-mix(in srgb, var(--accent) 70%, transparent);
pointer-events: none;
// slow on the way DOWN (this rule applies once the hover ends)
transition:
transform 1.4s ease-out,
background 1.4s ease-out;
}
&:hover::before {
background: var(--accent-2);
transform: translateZ(34px);
// instant on the way UP
transition-duration: 0.08s;
}
}
}