Hover card fan
Pure CSSThree overlapping cards: the one you point at straightens and comes toward you while the others dim and lean away. Static strips catch the pointer, so nothing flickers.
Six tiles that lift and flip to show their back. Odd tiles turn sideways, even ones head over heels, from a single custom property holding the rotation.
Your edited version
.tile-inner wrapper inside it flips, on :hover / :focus-visible.--flip, holds the whole rotation function (rotateY(180deg) or rotateX(180deg)), set once per tile so a single rule can flip every tile differently.--flip in its resting state, so once the tile turns, the back lands right-way up instead of mirrored.backface-visibility: hidden on both faces means only the one currently facing you is ever visible.translateZ before the flip keeps the tile from clipping into its neighbours mid-turn.--u, so the grid is the same share of a gallery card, the editor and a recording canvas. The lifted tile is the widest the model gets, because the perspective magnifies whatever comes toward you, so that is the pose the band is measured against.a transform function stored in --flipstatic tile, flipping innercontainer pointer-events: nonebackface-visibility: hidden<div class="scene">
<div class="grid">
<div class="tile" tabindex="0" style="--c:#8b6cff">
<div class="tile-inner"><b>01</b><span>tilt</span></div>
</div>
<div class="tile" tabindex="0" style="--c:#ff4d9d">
<div class="tile-inner"><b>02</b><span>flip</span></div>
</div>
<div class="tile" tabindex="0" style="--c:#2ee6d6;--on:#10131f">
<div class="tile-inner"><b>03</b><span>spin</span></div>
</div>
<div class="tile" tabindex="0" style="--c:#ffb547;--on:#10131f">
<div class="tile-inner"><b>04</b><span>lift</span></div>
</div>
<div class="tile" tabindex="0" style="--c:#8b6cff">
<div class="tile-inner"><b>05</b><span>fold</span></div>
</div>
<div class="tile" tabindex="0" style="--c:#ff4d9d">
<div class="tile-inner"><b>06</b><span>zoom</span></div>
</div>
</div>
</div>
.scene {
/* one base unit: every length below is a multiple of it, so the grid is the same share of a
card, the editor, a full screen and a recording canvas */
--u: 0.38vmin;
perspective: calc(800 * var(--u));
}
.grid {
display: grid;
grid-template-columns: repeat(3, calc(62 * var(--u)));
gap: calc(9 * var(--u));
transform-style: preserve-3d;
transform: rotateX(16deg);
/* the grid and its tiles are coplanar: take the grid out of hit-testing so the
pointer always resolves to a tile, never something ambiguous between them */
pointer-events: none;
}
.tile {
--flip: rotateY(180deg); /* odd tiles flip sideways */
position: relative;
height: calc(62 * var(--u));
outline: none;
cursor: pointer;
pointer-events: auto;
transform-style: preserve-3d;
}
.tile:nth-child(even) {
--flip: rotateX(180deg); /* even tiles flip head over heels */
}
.tile-inner {
position: absolute;
inset: 0;
pointer-events: none;
transform-style: preserve-3d;
transition: transform 0.6s cubic-bezier(0.3, 1.25, 0.5, 1);
}
.tile-inner > * {
position: absolute;
inset: 0;
display: grid;
place-items: center;
border-radius: calc(10 * var(--u));
backface-visibility: hidden;
}
/* front */
/* the front is see-through, so its number is really on the stage: it takes the stage's ink */
.tile-inner b {
font-size: calc(20 * var(--u));
font-weight: 900;
background: color-mix(in srgb, var(--c) 26%, transparent);
border: calc(1 * var(--u)) solid color-mix(in srgb, var(--c) 75%, transparent);
box-shadow: inset 0 0 calc(24 * var(--u)) color-mix(in srgb, var(--c) 30%, transparent);
}
/* back: pre-turned with the same rotation the tile will make, so it lands the right way up */
.tile-inner span {
background: linear-gradient(135deg, var(--c), color-mix(in srgb, var(--c) 45%, #000));
/* the back is solid, so its word is set for its own colour: white on violet and pink, dark
on teal and amber, where white would not read */
color: var(--on, #fff);
font-size: calc(12 * var(--u));
font-weight: 800;
letter-spacing: 0.04em;
transform: var(--flip);
}
.tile:hover .tile-inner,
.tile:focus-visible .tile-inner {
/* lift first (toward the viewer), then flip in place */
transform: translateZ(calc(30 * var(--u))) var(--flip);
}