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.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 {
perspective: 800px;
}
/* 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: 210px;
height: 168px;
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: 5px; --z: -14px; }
.slot:nth-child(3) { --c: #2ee6d6; --ry: -32deg; --x: -5px; --z: -14px; }
/* ...the middle one stands in front */
.slot:nth-child(2) { --c: #ff4d9d; --y: -6px; --z: 34px; }
.card {
position: absolute;
top: 14px;
left: calc(50% - 33px);
display: grid;
align-content: start;
justify-items: center;
gap: 6px;
width: 66px;
height: 140px;
padding: 10px 7px;
border: 1px solid color-mix(in srgb, var(--c) 70%, transparent);
border-radius: 12px;
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 18px color-mix(in srgb, var(--c) 22%, transparent),
0 14px 18px -14px #000;
color: #eceefb;
text-align: center;
pointer-events: none;
transform: translate3d(var(--x, 0px), var(--y, 0px), var(--z, 0px)) rotateY(var(--ry, 0deg));
transition: transform 0.45s cubic-bezier(0.3, 1.3, 0.5, 1);
}
/* plan name */
.card small {
color: var(--c);
font-size: 8px;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
}
/* price */
.card b {
font-size: 21px;
line-height: 1;
}
.card b sup {
font-size: 10px;
vertical-align: 7px;
}
/* feature lines */
.card span {
width: 100%;
height: 4px;
border-radius: 2px;
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: 8px;
padding: 4px 0;
border-radius: 99px;
background: var(--c);
color: #fff;
font-size: 8px;
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: 0px;
--y: -4px;
--z: 84px;
--ry: 0deg;
}
// Same hit-target trick as any hover demo in 3D: three static columns catch the pointer,
// the cards inside them are pointer-events:none and free to turn and come forward.
.d-pricing {
display: flex;
width: 210px;
height: 168px;
transform-style: preserve-3d;
pointer-events: none; // coplanar with the columns
&__slot {
--c: var(--accent);
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...
&:nth-child(1) {
--ry: 32deg;
--x: 5px;
--z: -14px;
}
&:nth-child(3) {
--c: var(--accent-2);
--ry: -32deg;
--x: -5px;
--z: -14px;
}
// ...the middle one stands in front.
&:nth-child(2) {
--c: var(--hot);
--y: -6px;
--z: 34px;
}
}
&__card {
position: absolute;
top: 14px;
left: calc(50% - 33px);
display: grid;
align-content: start;
justify-items: center;
gap: 6px;
width: 66px;
height: 140px;
padding: 10px 7px;
border: 1px solid color-mix(in srgb, var(--c) 70%, transparent);
border-radius: 12px;
background: linear-gradient(
170deg,
color-mix(in srgb, var(--c) 30%, var(--surface-solid)),
color-mix(in srgb, var(--c) 8%, var(--surface-solid))
);
box-shadow:
inset 0 0 18px color-mix(in srgb, var(--c) 22%, transparent),
0 14px 18px -14px #000;
color: var(--text);
text-align: center;
pointer-events: none;
transform: translate3d(var(--x, 0px), var(--y, 0px), var(--z, 0px)) rotateY(var(--ry, 0deg));
transition: transform 0.45s cubic-bezier(0.3, 1.3, 0.5, 1);
// plan name
small {
color: var(--c);
font-size: 8px;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
}
// price
b {
font-size: 21px;
line-height: 1;
sup {
font-size: 10px;
vertical-align: 7px;
}
}
// feature lines
span {
width: 100%;
height: 4px;
border-radius: 2px;
background: color-mix(in srgb, var(--text) 22%, transparent);
&:nth-of-type(2) {
width: 78%;
}
&:nth-of-type(3) {
width: 58%;
}
}
// call to action
em {
align-self: end;
width: 100%;
margin-top: 8px;
padding: 4px 0;
border-radius: 99px;
background: var(--c);
color: #fff;
font-size: 8px;
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%, var(--surface-solid)), color-mix(in srgb, var(--accent) 45%, var(--surface-solid)));
color: #fff;
small {
color: #fff;
}
span {
background: rgb(255 255 255 / 0.4);
}
em {
background: #fff;
color: #2a1030;
}
}
// white on teal is hard to read
&__slot:nth-child(3) em {
color: #06222a;
}
// Active card: square to the viewer and well in front of the other two.
& &__slot:is(:hover, :focus-visible) {
--x: 0px;
--y: -4px;
--z: 84px;
--ry: 0deg;
}
}