23 CSS Flip Cards12 / 23

Pure CSSMIT licensed

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.

Published

Live Demo
Try it

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>
.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;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Flip Card from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Album Cover Iris Aperture Reveal Card
Source: https://codefronts.com/components/css-flip-cards/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.
## HTML
```html
<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>
```
## CSS
```css
.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.

Make it yours

  • Change the aperture origin by editing at 50% 50% to at 0% 50% (left edge) or at 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 .6s starting .1s later — 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.

Gotchas — read before shipping

  • clip-path on 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; use aria-hidden on 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

ChromeSafariFirefoxEdge
55+13.1+54+55+

clip-path: circle() is universally supported in evergreen browsers. iOS Safari requires -webkit-clip-path prefix only below v13.

Techniques used in this demo

Search CodeFronts

Loading…