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.
Three pricing cards standing in an arc: the outer two turn to face the centre, the featured one stands in front. The one you point at squares up and steps forward.
Your edited version
pointer-events: none so hovering never lands on a card mid-turn.rotateY-ed toward the middle one (an arc, not a flat row) and sit a touch further back on Z so the featured card can stand in front of them at rest.translateZ, not scale() — pushing a card toward the camera through real perspective grows it more convincingly at the edges than scaling flatly would.--ry: 0deg along with the position, so turning to face you and stepping forward happen as one interpolated transform, not two separate motions.--u, tied to the canvas, text included, so the row of plans is the same share of a gallery card, the editor and a recording canvas.rotateY toward the centretranslateZ for emphasis instead of scalestatic columns as hit targetsvariables drive one transform<div class="scene">
<div class="pricing">
<div class="slot" tabindex="0">
<div class="card"><small>Solo</small><b><sup>$</sup>9</b><span></span><span></span><span></span><em>Start</em></div>
</div>
<div class="slot" tabindex="0">
<div class="card"><small>Team</small><b><sup>$</sup>29</b><span></span><span></span><span></span><em>Popular</em></div>
</div>
<div class="slot" tabindex="0">
<div class="card"><small>Org</small><b><sup>$</sup>99</b><span></span><span></span><span></span><em>Talk</em></div>
</div>
</div>
</div>
.scene {
/* one base unit: every length below is a multiple of it, so the plans are the same share of a
card, the editor, a full screen and a recording canvas */
--u: 0.32vmin;
perspective: calc(800 * var(--u));
}
/* same hit-target trick as any hover demo in 3D: static columns catch the pointer,
the cards inside are pointer-events: none and free to turn and come forward */
.pricing {
display: flex;
width: calc(210 * var(--u));
height: calc(168 * var(--u));
transform-style: preserve-3d;
pointer-events: none;
}
.slot {
--c: #8b6cff;
position: relative;
flex: 1;
outline: none;
cursor: pointer;
pointer-events: auto;
transform-style: preserve-3d;
}
/* the arc: outer cards turn to face the centre and stand a little further back... */
.slot:nth-child(1) { --ry: 32deg; --x: calc(5 * var(--u)); --z: calc(-14 * var(--u)); }
.slot:nth-child(3) { --c: #2ee6d6; --ry: -32deg; --x: calc(-5 * var(--u)); --z: calc(-14 * var(--u)); }
/* ...the middle one stands in front */
.slot:nth-child(2) { --c: #ff4d9d; --y: calc(-6 * var(--u)); --z: calc(34 * var(--u)); }
.card {
position: absolute;
top: calc(14 * var(--u));
left: calc(50% - 33 * var(--u));
display: grid;
align-content: start;
justify-items: center;
gap: calc(6 * var(--u));
/* 66 × 140 outside, padding and border included: centred in its slot */
box-sizing: border-box;
width: calc(66 * var(--u));
height: calc(140 * var(--u));
padding: calc(10 * var(--u)) calc(7 * var(--u));
border: calc(1 * var(--u)) solid color-mix(in srgb, var(--c) 70%, transparent);
border-radius: calc(12 * var(--u));
background: linear-gradient(
170deg,
color-mix(in srgb, var(--c) 30%, #141830),
color-mix(in srgb, var(--c) 8%, #141830)
);
box-shadow:
inset 0 0 calc(18 * var(--u)) color-mix(in srgb, var(--c) 22%, transparent),
0 calc(14 * var(--u)) calc(18 * var(--u)) calc(-14 * var(--u)) #000;
color: #eceefb;
text-align: center;
pointer-events: none;
transform: translate3d(var(--x, 0), var(--y, 0), var(--z, 0)) rotateY(var(--ry, 0deg));
transition: transform 0.45s cubic-bezier(0.3, 1.3, 0.5, 1);
}
/* plan name */
.card small {
/* the plan's colour lifted towards white, so it reads on its dark glass card: the violet as it is
was 3:1 */
color: color-mix(in srgb, var(--c), #fff 45%);
font-size: calc(8 * var(--u));
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
}
/* price */
.card b {
font-size: calc(21 * var(--u));
line-height: 1;
}
.card b sup {
font-size: calc(10 * var(--u));
vertical-align: calc(7 * var(--u));
}
/* feature lines */
.card span {
width: 100%;
height: calc(4 * var(--u));
border-radius: calc(2 * var(--u));
background: color-mix(in srgb, #eceefb 22%, transparent);
}
.card span:nth-of-type(2) { width: 78%; }
.card span:nth-of-type(3) { width: 58%; }
/* call to action */
.card em {
align-self: end;
width: 100%;
margin-top: calc(8 * var(--u));
padding: calc(4 * var(--u)) 0;
border-radius: calc(99 * var(--u));
/* the plan's colour deepened, so its white word reads: on the violet as it is, 3.5:1 */
background: color-mix(in srgb, var(--c), #000 28%);
color: #fff;
font-size: calc(8 * var(--u));
font-style: normal;
font-weight: 800;
}
/* the featured plan is filled instead of glassy */
.slot:nth-child(2) .card {
border-color: var(--c);
background: linear-gradient(170deg, color-mix(in srgb, var(--c) 70%, #141830), color-mix(in srgb, #8b6cff 45%, #141830));
color: #fff;
}
.slot:nth-child(2) .card small { color: #fff; }
.slot:nth-child(2) .card span { background: rgb(255 255 255 / 0.4); }
.slot:nth-child(2) .card em { background: #fff; color: #2a1030; }
/* white on teal is hard to read */
.slot:nth-child(3) .card em {
color: #06222a;
}
/* active card: square to the viewer and well in front of the other two */
.pricing .slot:is(:hover, :focus-visible) {
--x: 0;
--y: calc(-4 * var(--u));
--z: calc(84 * var(--u));
--ry: 0deg;
}