Cards & galleries
Flip card
Pure CSSHover, tap or focus to flip. The back face is hidden until it turns toward you.
Five cards share one pivot below the hand and fan out by index.
Your edited version
transform-origin: 50% 170% moves the pivot far below the card, so rotateZ swings it along an arc instead of spinning it in place.rotateZ(calc(var(--i) * 16deg)) fans symmetrically around an upright middle card.translateZ per card gives each its own depth, which avoids flicker where they overlap.transform-origin far below the cardrotateZ(var(--i) × step)translateZ per card to avoid z-fighting<div class="scene">
<div class="hand">
<i style="--i:-2">A<small>♠</small></i>
<i style="--i:-1">K<small>♠</small></i>
<i style="--i:0">Q<small>♠</small></i>
<i style="--i:1">J<small>♠</small></i>
<i style="--i:2">10<small>♠</small></i>
</div>
</div>
.scene {
perspective: 800px;
}
.hand {
position: relative;
width: 110px;
height: 156px;
transform-style: preserve-3d;
transform: rotateX(26deg);
}
.hand i {
position: absolute;
inset: 0;
padding: 8px 12px;
border-radius: 10px;
background: linear-gradient(160deg, #fff, #d9dcec);
color: #14172b;
font: 800 1.6rem/1 system-ui;
box-shadow: 0 8px 16px -8px #000;
transform-origin: 50% 170%;
animation: fan 3s ease-in-out infinite alternate;
}
.hand small { display: block; font-size: 1.2rem; }
@keyframes fan {
0%, 15% { transform: translateZ(calc(var(--i) * 1px)) rotateZ(0deg); }
85%, 100% { transform: translateZ(calc(var(--i) * 6px)) rotateZ(calc(var(--i) * 16deg)); }
}
.d-cardfan {
position: relative;
width: 84px;
height: 120px;
transform-style: preserve-3d;
transform: translateY(10px) rotateX(26deg);
// --i runs -2…2 from the markup, so the middle card stays upright.
i {
position: absolute;
inset: 0;
padding: 6px 9px;
border-radius: 9px;
background: linear-gradient(160deg, #fff, #d9dcec);
color: #14172b;
font-size: 20px;
font-style: normal;
font-weight: 800;
line-height: 1;
box-shadow: 0 8px 16px -8px #000;
// The pivot sits well below the card, like the wrist holding the hand.
transform-origin: 50% 170%;
animation: d-cardfan 3s ease-in-out infinite alternate;
small {
display: block;
font-size: 16px;
}
}
}
@keyframes d-cardfan {
0%,
15% {
transform: translateZ(calc(var(--i) * 1px)) rotateZ(0deg);
}
85%,
100% {
transform: translateZ(calc(var(--i) * 6px)) rotateZ(calc(var(--i) * 16deg));
}
}