23 CSS Flip Cards 12 / 23
Album Cover Iris Aperture Reveal Card
Front face shrinks via clip-path circle while back expands from center — a camera-aperture reveal with zero 3D rotation, proving 'flip card' can mean visual swap, not just axis spin.
The code
<div class="fc-21">
<div class="fc-21__scene">
<div class="fc-21__card">
<div class="fc-21__front">
<div class="fc-21__cover">
<svg width="220" height="220" viewBox="0 0 220 220" fill="none">
<defs>
<radialGradient id="fc-21-cover-bg" cx="0.4" cy="0.35" r="0.7">
<stop offset="0%" stop-color="#ec4899"/>
<stop offset="40%" stop-color="#a21caf"/>
<stop offset="100%" stop-color="#1e1b4b"/>
</radialGradient>
<linearGradient id="fc-21-vinyl" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#1c1c1c"/>
<stop offset="100%" stop-color="#0a0a0a"/>
</linearGradient>
</defs>
<rect width="220" height="220" fill="url(#fc-21-cover-bg)"/>
<circle cx="110" cy="110" r="78" fill="url(#fc-21-vinyl)"/>
<circle cx="110" cy="110" r="78" fill="none" stroke="#3a3a3a" stroke-width="0.5"/>
<circle cx="110" cy="110" r="68" fill="none" stroke="#2a2a2a" stroke-width="0.5"/>
<circle cx="110" cy="110" r="58" fill="none" stroke="#2a2a2a" stroke-width="0.5"/>
<circle cx="110" cy="110" r="48" fill="none" stroke="#2a2a2a" stroke-width="0.5"/>
<circle cx="110" cy="110" r="38" fill="none" stroke="#2a2a2a" stroke-width="0.5"/>
<circle cx="110" cy="110" r="28" fill="#ec4899"/>
<circle cx="110" cy="110" r="4" fill="#0a0a0a"/>
<g opacity="0.4">
<circle cx="170" cy="50" r="2" fill="#fef3c7"/>
<circle cx="180" cy="180" r="1.5" fill="#fef3c7"/>
<circle cx="40" cy="170" r="2" fill="#fef3c7"/>
<circle cx="30" cy="40" r="1.5" fill="#fef3c7"/>
</g>
</svg>
</div>
<div class="fc-21__cover-meta">
<div class="fc-21__artist">VANTABLACK</div>
<div class="fc-21__album">After Midnight Drives</div>
<div class="fc-21__pill-row">
<span class="fc-21__pill">SYNTHWAVE</span>
<span class="fc-21__pill">2026 · LP</span>
</div>
</div>
<div class="fc-21__hint">Hover for the tracklist ◐</div>
</div>
<div class="fc-21__back">
<div class="fc-21__back-head">
<span class="fc-21__back-side">SIDE A</span>
<span class="fc-21__back-runtime">38 : 12</span>
</div>
<div class="fc-21__tracklist">
<div class="fc-21__track"><span class="fc-21__t-num">01</span><span class="fc-21__t-name">Neon Highway</span><span class="fc-21__t-time">4:18</span></div>
<div class="fc-21__track"><span class="fc-21__t-num">02</span><span class="fc-21__t-name">Cassette Memory</span><span class="fc-21__t-time">5:42</span></div>
<div class="fc-21__track"><span class="fc-21__t-num">03</span><span class="fc-21__t-name">Static Bloom</span><span class="fc-21__t-time">3:55</span></div>
<div class="fc-21__track"><span class="fc-21__t-num">04</span><span class="fc-21__t-name">2 AM in Tokyo</span><span class="fc-21__t-time">6:08</span></div>
<div class="fc-21__track"><span class="fc-21__t-num">05</span><span class="fc-21__t-name">Lavender Static</span><span class="fc-21__t-time">4:33</span></div>
<div class="fc-21__track"><span class="fc-21__t-num">06</span><span class="fc-21__t-name">After Midnight</span><span class="fc-21__t-time">13:36</span></div>
</div>
<button class="fc-21__play">▶ Play full album</button>
</div>
</div>
</div>
</div> <div class="fc-21">
<div class="fc-21__scene">
<div class="fc-21__card">
<div class="fc-21__front">
<div class="fc-21__cover">
<svg width="220" height="220" viewBox="0 0 220 220" fill="none">
<defs>
<radialGradient id="fc-21-cover-bg" cx="0.4" cy="0.35" r="0.7">
<stop offset="0%" stop-color="#ec4899"/>
<stop offset="40%" stop-color="#a21caf"/>
<stop offset="100%" stop-color="#1e1b4b"/>
</radialGradient>
<linearGradient id="fc-21-vinyl" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#1c1c1c"/>
<stop offset="100%" stop-color="#0a0a0a"/>
</linearGradient>
</defs>
<rect width="220" height="220" fill="url(#fc-21-cover-bg)"/>
<circle cx="110" cy="110" r="78" fill="url(#fc-21-vinyl)"/>
<circle cx="110" cy="110" r="78" fill="none" stroke="#3a3a3a" stroke-width="0.5"/>
<circle cx="110" cy="110" r="68" fill="none" stroke="#2a2a2a" stroke-width="0.5"/>
<circle cx="110" cy="110" r="58" fill="none" stroke="#2a2a2a" stroke-width="0.5"/>
<circle cx="110" cy="110" r="48" fill="none" stroke="#2a2a2a" stroke-width="0.5"/>
<circle cx="110" cy="110" r="38" fill="none" stroke="#2a2a2a" stroke-width="0.5"/>
<circle cx="110" cy="110" r="28" fill="#ec4899"/>
<circle cx="110" cy="110" r="4" fill="#0a0a0a"/>
<g opacity="0.4">
<circle cx="170" cy="50" r="2" fill="#fef3c7"/>
<circle cx="180" cy="180" r="1.5" fill="#fef3c7"/>
<circle cx="40" cy="170" r="2" fill="#fef3c7"/>
<circle cx="30" cy="40" r="1.5" fill="#fef3c7"/>
</g>
</svg>
</div>
<div class="fc-21__cover-meta">
<div class="fc-21__artist">VANTABLACK</div>
<div class="fc-21__album">After Midnight Drives</div>
<div class="fc-21__pill-row">
<span class="fc-21__pill">SYNTHWAVE</span>
<span class="fc-21__pill">2026 · LP</span>
</div>
</div>
<div class="fc-21__hint">Hover for the tracklist ◐</div>
</div>
<div class="fc-21__back">
<div class="fc-21__back-head">
<span class="fc-21__back-side">SIDE A</span>
<span class="fc-21__back-runtime">38 : 12</span>
</div>
<div class="fc-21__tracklist">
<div class="fc-21__track"><span class="fc-21__t-num">01</span><span class="fc-21__t-name">Neon Highway</span><span class="fc-21__t-time">4:18</span></div>
<div class="fc-21__track"><span class="fc-21__t-num">02</span><span class="fc-21__t-name">Cassette Memory</span><span class="fc-21__t-time">5:42</span></div>
<div class="fc-21__track"><span class="fc-21__t-num">03</span><span class="fc-21__t-name">Static Bloom</span><span class="fc-21__t-time">3:55</span></div>
<div class="fc-21__track"><span class="fc-21__t-num">04</span><span class="fc-21__t-name">2 AM in Tokyo</span><span class="fc-21__t-time">6:08</span></div>
<div class="fc-21__track"><span class="fc-21__t-num">05</span><span class="fc-21__t-name">Lavender Static</span><span class="fc-21__t-time">4:33</span></div>
<div class="fc-21__track"><span class="fc-21__t-num">06</span><span class="fc-21__t-name">After Midnight</span><span class="fc-21__t-time">13:36</span></div>
</div>
<button class="fc-21__play">▶ Play full album</button>
</div>
</div>
</div>
</div>.fc-21,.fc-21 *,.fc-21 *::before,.fc-21 *::after{box-sizing:border-box;margin:0;padding:0}
.fc-21 ::selection{background:#ec4899;color:#0a0a0a}
.fc-21{
--bg:#0a0612;--neon:#ec4899;--violet:#a21caf;--cream:#fef3c7;--ink:#f5e8ff;
--card-w:360px;--card-h:500px;
font-family:'Inter',system-ui,-apple-system,sans-serif;
background:radial-gradient(ellipse at 30% 20%,#1e0b3b,#0a0612 70%);
min-height:100vh;display:flex;align-items:center;justify-content:center;
padding:40px 20px;color:var(--ink);
}
.fc-21__scene{width:var(--card-w);height:var(--card-h)}
.fc-21__card{width:100%;height:100%;position:relative;border-radius:8px;overflow:hidden;box-shadow:0 24px 60px rgba(0,0,0,.6),0 0 0 1px rgba(236,72,153,.2)}
.fc-21__front,.fc-21__back{
position:absolute;inset:0;
display:flex;flex-direction:column;align-items:center;
padding:30px 28px;
transition:clip-path .65s cubic-bezier(.7,0,.3,1);
}
.fc-21__front{
background:linear-gradient(160deg,#1e0b3b 0%,#0a0612 100%);
clip-path:circle(150% at 50% 50%);
-webkit-clip-path:circle(150% at 50% 50%);
z-index:1;
}
.fc-21__back{
background:linear-gradient(160deg,#0a0612 0%,#1e0b3b 100%);
clip-path:circle(0% at 50% 50%);
-webkit-clip-path:circle(0% at 50% 50%);
z-index:2;
}
.fc-21__scene:hover .fc-21__front{clip-path:circle(0% at 50% 50%);-webkit-clip-path:circle(0% at 50% 50%)}
.fc-21__scene:hover .fc-21__back{clip-path:circle(150% at 50% 50%);-webkit-clip-path:circle(150% at 50% 50%)}
/* FRONT — album cover */
.fc-21__cover{margin-top:6px;border-radius:4px;overflow:hidden;box-shadow:0 14px 30px rgba(0,0,0,.5)}
.fc-21__cover-meta{margin-top:22px;text-align:center;width:100%}
.fc-21__artist{font-size:11px;letter-spacing:.45em;color:var(--neon);font-weight:700}
.fc-21__album{font-size:23px;font-weight:800;color:var(--ink);margin-top:6px;letter-spacing:-.02em;font-family:'Playfair Display',Georgia,serif;font-style:italic}
.fc-21__pill-row{display:flex;justify-content:center;gap:6px;margin-top:12px}
.fc-21__pill{
padding:4px 11px;border:1px solid rgba(236,72,153,.4);border-radius:999px;
font-size:9px;letter-spacing:.2em;text-transform:uppercase;color:rgba(245,232,255,.7);font-weight:600;
}
.fc-21__hint{margin-top:auto;font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:rgba(245,232,255,.4)}
/* BACK — tracklist */
.fc-21__back-head{
display:flex;justify-content:space-between;width:100%;
padding-bottom:14px;margin-bottom:14px;
border-bottom:1px solid rgba(236,72,153,.25);
font-size:10px;letter-spacing:.3em;text-transform:uppercase;color:var(--neon);font-weight:700;
}
.fc-21__back-runtime{color:rgba(245,232,255,.55);font-family:ui-monospace,monospace;letter-spacing:.1em}
.fc-21__tracklist{display:flex;flex-direction:column;gap:1px;width:100%;flex:1}
.fc-21__track{
display:grid;grid-template-columns:28px 1fr auto;align-items:center;gap:12px;
padding:10px 8px;border-radius:6px;
transition:background .15s;cursor:pointer;
}
.fc-21__track:hover{background:rgba(236,72,153,.08)}
.fc-21__t-num{font-family:ui-monospace,monospace;font-size:10px;color:rgba(245,232,255,.4);letter-spacing:.1em}
.fc-21__t-name{font-size:13px;color:var(--ink);font-weight:500}
.fc-21__t-time{font-family:ui-monospace,monospace;font-size:11px;color:rgba(245,232,255,.4);letter-spacing:.05em}
.fc-21__play{
margin-top:14px;width:100%;padding:13px 0;
border:1px solid rgba(236,72,153,.5);border-radius:8px;
background:linear-gradient(135deg,rgba(236,72,153,.18),rgba(162,28,175,.12));
color:var(--neon);font-size:12px;letter-spacing:.25em;text-transform:uppercase;font-weight:700;
cursor:pointer;transition:background .2s,box-shadow .2s;
font-family:inherit;
}
.fc-21__play:hover{background:linear-gradient(135deg,rgba(236,72,153,.3),rgba(162,28,175,.2));box-shadow:0 8px 24px rgba(236,72,153,.25)}
@media(prefers-reduced-motion:reduce){
.fc-21__front,.fc-21__back{transition:none}
} .fc-21,.fc-21 *,.fc-21 *::before,.fc-21 *::after{box-sizing:border-box;margin:0;padding:0}
.fc-21 ::selection{background:#ec4899;color:#0a0a0a}
.fc-21{
--bg:#0a0612;--neon:#ec4899;--violet:#a21caf;--cream:#fef3c7;--ink:#f5e8ff;
--card-w:360px;--card-h:500px;
font-family:'Inter',system-ui,-apple-system,sans-serif;
background:radial-gradient(ellipse at 30% 20%,#1e0b3b,#0a0612 70%);
min-height:100vh;display:flex;align-items:center;justify-content:center;
padding:40px 20px;color:var(--ink);
}
.fc-21__scene{width:var(--card-w);height:var(--card-h)}
.fc-21__card{width:100%;height:100%;position:relative;border-radius:8px;overflow:hidden;box-shadow:0 24px 60px rgba(0,0,0,.6),0 0 0 1px rgba(236,72,153,.2)}
.fc-21__front,.fc-21__back{
position:absolute;inset:0;
display:flex;flex-direction:column;align-items:center;
padding:30px 28px;
transition:clip-path .65s cubic-bezier(.7,0,.3,1);
}
.fc-21__front{
background:linear-gradient(160deg,#1e0b3b 0%,#0a0612 100%);
clip-path:circle(150% at 50% 50%);
-webkit-clip-path:circle(150% at 50% 50%);
z-index:1;
}
.fc-21__back{
background:linear-gradient(160deg,#0a0612 0%,#1e0b3b 100%);
clip-path:circle(0% at 50% 50%);
-webkit-clip-path:circle(0% at 50% 50%);
z-index:2;
}
.fc-21__scene:hover .fc-21__front{clip-path:circle(0% at 50% 50%);-webkit-clip-path:circle(0% at 50% 50%)}
.fc-21__scene:hover .fc-21__back{clip-path:circle(150% at 50% 50%);-webkit-clip-path:circle(150% at 50% 50%)}
/* FRONT — album cover */
.fc-21__cover{margin-top:6px;border-radius:4px;overflow:hidden;box-shadow:0 14px 30px rgba(0,0,0,.5)}
.fc-21__cover-meta{margin-top:22px;text-align:center;width:100%}
.fc-21__artist{font-size:11px;letter-spacing:.45em;color:var(--neon);font-weight:700}
.fc-21__album{font-size:23px;font-weight:800;color:var(--ink);margin-top:6px;letter-spacing:-.02em;font-family:'Playfair Display',Georgia,serif;font-style:italic}
.fc-21__pill-row{display:flex;justify-content:center;gap:6px;margin-top:12px}
.fc-21__pill{
padding:4px 11px;border:1px solid rgba(236,72,153,.4);border-radius:999px;
font-size:9px;letter-spacing:.2em;text-transform:uppercase;color:rgba(245,232,255,.7);font-weight:600;
}
.fc-21__hint{margin-top:auto;font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:rgba(245,232,255,.4)}
/* BACK — tracklist */
.fc-21__back-head{
display:flex;justify-content:space-between;width:100%;
padding-bottom:14px;margin-bottom:14px;
border-bottom:1px solid rgba(236,72,153,.25);
font-size:10px;letter-spacing:.3em;text-transform:uppercase;color:var(--neon);font-weight:700;
}
.fc-21__back-runtime{color:rgba(245,232,255,.55);font-family:ui-monospace,monospace;letter-spacing:.1em}
.fc-21__tracklist{display:flex;flex-direction:column;gap:1px;width:100%;flex:1}
.fc-21__track{
display:grid;grid-template-columns:28px 1fr auto;align-items:center;gap:12px;
padding:10px 8px;border-radius:6px;
transition:background .15s;cursor:pointer;
}
.fc-21__track:hover{background:rgba(236,72,153,.08)}
.fc-21__t-num{font-family:ui-monospace,monospace;font-size:10px;color:rgba(245,232,255,.4);letter-spacing:.1em}
.fc-21__t-name{font-size:13px;color:var(--ink);font-weight:500}
.fc-21__t-time{font-family:ui-monospace,monospace;font-size:11px;color:rgba(245,232,255,.4);letter-spacing:.05em}
.fc-21__play{
margin-top:14px;width:100%;padding:13px 0;
border:1px solid rgba(236,72,153,.5);border-radius:8px;
background:linear-gradient(135deg,rgba(236,72,153,.18),rgba(162,28,175,.12));
color:var(--neon);font-size:12px;letter-spacing:.25em;text-transform:uppercase;font-weight:700;
cursor:pointer;transition:background .2s,box-shadow .2s;
font-family:inherit;
}
.fc-21__play:hover{background:linear-gradient(135deg,rgba(236,72,153,.3),rgba(162,28,175,.2));box-shadow:0 8px 24px rgba(236,72,153,.25)}
@media(prefers-reduced-motion:reduce){
.fc-21__front,.fc-21__back{transition:none}
}How this works
This demo deliberately breaks from 3D rotation entirely. There's no perspective, no rotateY, no backface-visibility. Both faces sit absolutely on top of each other and the transition uses clip-path: circle() to reveal them.
At rest the front face has clip-path: circle(150% at 50% 50%) (fully visible — the circle is larger than the card) and the back has clip-path: circle(0% at 50% 50%) (zero size, fully clipped). On hover both transition simultaneously: front shrinks to circle(0%) while back expands to circle(150%). The result reads like a camera aperture closing on one image and opening on another — clean, modern, and instantly recognisable as photography / film UI.
The technique is fully CSS-only and animates a single property per face. Because clip-path on a circle is GPU-composited, the transition stays at 60fps even on low-end devices — much cheaper than 3D rotation with backface-visibility.
Customize
- Change the aperture origin by editing
at 50% 50%toat 0% 50%(left edge) orat 50% 0%(top edge) — the reveal sweeps in from that corner instead of opening centre-out. - Replace circle with ellipse for a more cinematic aspect:
clip-path: ellipse(0% 50% at 50% 50%)reveals as a horizontal slit before opening fully. - Add a polygon shape for a geometric iris:
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)creates a diamond reveal that scales to fill via transform. - Stagger the front/back transitions slightly — front shrinks in
.4s, back expands in.6sstarting.1slater — to make the reveal feel less synchronous and more like a real shutter. - Pair the iris with a colour wash by setting
filter: hue-rotate(20deg) saturate(1.2)on the back face — gives the revealed content a richer, film-like saturation pop.
Watch out for
clip-pathon circle is GPU-accelerated in Chromium and WebKit but Firefox under v100 falls back to CPU clipping which can stutter on busy pages — test in Firefox before shipping.clip-path: circle(0%)still keeps the element in the accessibility tree — screen readers will read both front and back content even when one is invisible; usearia-hiddenon the hidden face for an a11y-friendly version.- If the back face contains text that overflows when clipped, the text reflows during the transition and looks janky — keep back content centred and within a comfortable margin so the clip never bisects a word mid-frame.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 55+ | 13.1+ | 54+ | 55+ |
clip-path: circle() is universally supported in evergreen browsers. iOS Safari requires -webkit-clip-path prefix only below v13.