23 CSS Flip Cards 11 / 23

Photo Gallery Overlay Card

Front displays a full-bleed SVG aurora landscape scene; back reveals location metadata, camera EXIF settings, content tags, and a download CTA.

Pure CSS MIT licensed
Live Demo Open in tab
Open in playground

The code

<div class="fc-09">
  <div class="fc-09__scene">
    <div class="fc-09__card">
      <div class="fc-09__front">
        <div class="fc-09__photo">
          <svg viewBox="0 0 340 380" preserveAspectRatio="xMidYMid slice" fill="none">
            <!-- sky gradient -->
            <defs>
              <linearGradient id="sky9" x1="0" y1="0" x2="0" y2="1" gradientUnits="objectBoundingBox">
                <stop stop-color="#041830"/>
                <stop offset=".5" stop-color="#0a2a4a"/>
                <stop offset="1" stop-color="#0d3a5c"/>
              </linearGradient>
              <linearGradient id="water9" x1="0" y1="0" x2="0" y2="1" gradientUnits="objectBoundingBox">
                <stop stop-color="#062035"/>
                <stop offset="1" stop-color="#020d18"/>
              </linearGradient>
            </defs>
            <rect width="340" height="380" fill="url(#sky9)"/>
            <!-- stars -->
            <circle cx="30" cy="30" r="1.5" fill="white" opacity=".8"/>
            <circle cx="80" cy="15" r="1" fill="white" opacity=".6"/>
            <circle cx="150" cy="25" r="1.5" fill="white" opacity=".9"/>
            <circle cx="220" cy="10" r="1" fill="white" opacity=".7"/>
            <circle cx="290" cy="35" r="1.5" fill="white" opacity=".8"/>
            <circle cx="310" cy="15" r="1" fill="white" opacity=".5"/>
            <circle cx="60" cy="60" r="1" fill="white" opacity=".6"/>
            <circle cx="260" cy="60" r="1.5" fill="white" opacity=".7"/>
            <!-- aurora band -->
            <ellipse cx="170" cy="80" rx="200" ry="40" fill="rgba(20,184,166,.15)"/>
            <ellipse cx="170" cy="90" rx="160" ry="25" fill="rgba(14,165,233,.1)"/>
            <!-- mountains far -->
            <path d="M0 220 L60 140 L120 200 L180 130 L240 190 L300 140 L340 180 L340 280 L0 280Z" fill="rgba(12,36,64,.7)"/>
            <!-- mountains near -->
            <path d="M0 260 L80 180 L140 230 L200 170 L260 220 L340 175 L340 280 L0 280Z" fill="rgba(8,24,48,.9)"/>
            <!-- water reflection -->
            <rect x="0" y="280" width="340" height="100" fill="url(#water9)"/>
            <!-- reflection lines -->
            <line x1="50" y1="300" x2="290" y2="300" stroke="rgba(14,165,233,.08)" stroke-width="8"/>
            <line x1="30" y1="320" x2="310" y2="320" stroke="rgba(14,165,233,.05)" stroke-width="12"/>
            <!-- aurora reflection in water -->
            <ellipse cx="170" cy="330" rx="180" ry="20" fill="rgba(20,184,166,.08)"/>
            <!-- moon -->
            <circle cx="260" cy="55" r="20" fill="rgba(240,249,255,.9)"/>
            <circle cx="268" cy="48" r="16" fill="rgba(10,28,48,.95)"/>
          </svg>
          <div class="fc-09__photo-meta">
            <div class="fc-09__cam-icon">
              <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
              f/2.8 · 25s · ISO 3200
            </div>
            <div class="fc-09__photo-title">Aurora at Jökulsárlón</div>
          </div>
        </div>
        <div class="fc-09__front-hint">Hover for details & download →</div>
      </div>
      <div class="fc-09__back">
        <div class="fc-09__back-img-thumb">
          <svg viewBox="0 0 340 120" preserveAspectRatio="xMidYMid slice" fill="none">
            <rect width="340" height="120" fill="#041830"/>
            <path d="M0 80 L80 50 L140 70 L200 40 L260 65 L340 45 L340 120 L0 120Z" fill="rgba(12,36,64,.9)"/>
            <ellipse cx="170" cy="30" rx="160" ry="18" fill="rgba(20,184,166,.2)"/>
            <rect x="0" y="90" width="340" height="30" fill="rgba(6,18,30,.9)"/>
          </svg>
        </div>
        <div class="fc-09__detail-grid">
          <div class="fc-09__detail"><div class="fc-09__detail-lbl">Location</div><div class="fc-09__detail-val">Iceland</div></div>
          <div class="fc-09__detail"><div class="fc-09__detail-lbl">Date Captured</div><div class="fc-09__detail-val">Nov 12, 2024</div></div>
          <div class="fc-09__detail"><div class="fc-09__detail-lbl">Camera</div><div class="fc-09__detail-val">Sony A7R V</div></div>
          <div class="fc-09__detail"><div class="fc-09__detail-lbl">Resolution</div><div class="fc-09__detail-val">61 MP RAW</div></div>
        </div>
        <div class="fc-09__tags-row">
          <span class="fc-09__photo-tag">Aurora</span>
          <span class="fc-09__photo-tag">Landscape</span>
          <span class="fc-09__photo-tag">Night Sky</span>
        </div>
        <button class="fc-09__download">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
          Download Full Resolution
        </button>
      </div>
    </div>
  </div>
</div>
.fc-09,.fc-09 *,.fc-09 *::before,.fc-09 *::after{box-sizing:border-box;margin:0;padding:0}
.fc-09 ::selection{background:#0ea5e9;color:#fff}
.fc-09{
  --bg:#080c10;--sky:#0ea5e9;--teal:#14b8a6;--white:#f0f9ff;
  --card-w:340px;--card-h:440px;
  font-family:'Segoe UI',system-ui,sans-serif;
  background:radial-gradient(ellipse at 50% 0%,#061020,#080c10 65%);
  min-height:100vh;display:flex;align-items:center;justify-content:center;
  padding:40px 20px;color:var(--white);
}
.fc-09__scene{width:var(--card-w);height:var(--card-h);perspective:1200px;cursor:pointer}
.fc-09__card{width:100%;height:100%;position:relative;transform-style:preserve-3d;transform-origin:center top;transition:transform .85s cubic-bezier(.4,0,.2,1)}
/* See note on translateY counter — same as fc-02 (flip-up) but inverted:
   rotating around top edge would push the card UP by its full height; the
   translateY(100%) at the end keeps the visual position fixed. */
/* Fold-out from the TOP edge — the card unfolds downward like peeling
   back a Polaroid, instead of spinning around centre. Preserves the
   photo's visual focus during the transition. */
.fc-09__scene:hover .fc-09__card{transform:translateY(100%) rotateX(-180deg)}
.fc-09__front,.fc-09__back{position:absolute;inset:0;border-radius:20px;backface-visibility:hidden;-webkit-backface-visibility:hidden;overflow:hidden}
/* FRONT — full-bleed photo */
.fc-09__front{display:flex;flex-direction:column;border:1px solid rgba(14,165,233,.2)}
.fc-09__photo{
  flex:1;position:relative;overflow:hidden;
  background:linear-gradient(160deg,#0c2340,#041520,#0d2a2a);
}
/* SVG landscape scene */
.fc-09__photo svg{position:absolute;inset:0;width:100%;height:100%}
.fc-09__photo-meta{
  position:absolute;bottom:0;inset-x:0;
  padding:20px;
  background:linear-gradient(to top,rgba(8,12,16,.9),transparent);
}
.fc-09__cam-icon{display:flex;align-items:center;gap:6px;font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:rgba(240,249,255,.5)}
.fc-09__photo-title{font-size:20px;font-weight:800;color:var(--white);margin-top:4px}
.fc-09__front-hint{padding:14px;text-align:center;background:rgba(0,0,0,.4);font-size:10px;color:rgba(240,249,255,.3);letter-spacing:.08em}
/* BACK */
.fc-09__back{
  background:linear-gradient(160deg,#0a1520,#060e18);
  border:1px solid rgba(20,184,166,.25);
  /* Pre-rotated on X so the top-hinge fold-out brings it forward. */
  transform:rotateX(180deg);
  display:flex;flex-direction:column;padding:28px;gap:16px;
}
.fc-09__back-img-thumb{
  height:120px;border-radius:12px;overflow:hidden;
  background:linear-gradient(160deg,#0c2340,#041520,#0d2a2a);
  position:relative;flex-shrink:0;
}
.fc-09__back-img-thumb svg{position:absolute;inset:0;width:100%;height:100%}
.fc-09__detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.fc-09__detail{background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);border-radius:10px;padding:10px 12px}
.fc-09__detail-lbl{font-size:9px;letter-spacing:.12em;text-transform:uppercase;color:rgba(240,249,255,.35);margin-bottom:3px}
.fc-09__detail-val{font-size:13px;font-weight:600;color:var(--white)}
.fc-09__tags-row{display:flex;gap:6px;flex-wrap:wrap}
.fc-09__photo-tag{font-size:10px;padding:3px 10px;border-radius:20px;background:rgba(14,165,233,.1);border:1px solid rgba(14,165,233,.2);color:var(--sky)}
.fc-09__download{
  margin-top:auto;display:flex;align-items:center;justify-content:center;gap:8px;
  padding:13px;border-radius:12px;
  background:linear-gradient(135deg,var(--sky),var(--teal));
  color:#000;font-size:13px;font-weight:800;cursor:pointer;border:none;letter-spacing:.04em;
}
@media(prefers-reduced-motion:reduce){.fc-09__card{transition:none}}

How this works

The landscape uses a 7-layer SVG: a sky <linearGradient> fill, star circles, two aurora ellipses with rgba teal fills, two mountain paths with decreasing opacity for depth, a water rectangle, and a crescent moon from two overlapping circles. The SVG uses preserveAspectRatio="xMidYMid slice" to fill the card like object-fit:cover.

A linear-gradient(to top, rgba(...)) scrim at the bottom of the photo area makes overlay text readable regardless of sky brightness — a standard photography technique replicated purely in CSS. The back face uses a smaller version of the same SVG as a thumbnail anchor before the metadata grid.

Customize

  • Replace the SVG scene with a real image by setting background: url(photo.jpg) center/cover on .fc-09__photo and removing the SVG element.
  • Add a lightbox trigger on the front face so a click opens a full-screen overlay instead of flipping the card.
  • Create a photo grid by mapping a data array through the card HTML template and nesting multiple .fc-09__scene elements in a CSS Grid container.
  • Add EXIF icon badges (aperture f, shutter speed, ISO) using inline SVG symbols in a <defs> block for consistent sizing.
  • Animate the aurora with a @keyframes that oscillates the ellipse ry from 35px to 50px for a pulsing northern-lights effect.

Watch out for

  • SVG linearGradient with gradientUnits="objectBoundingBox" behaves unexpectedly on non-square SVGs — use userSpaceOnUse with absolute coordinates for reliable results.
  • The download button is presentational only — a real download requires a server-signed URL or a blob-URL created from a fetch response.
  • Placing a preserve-3d context on a parent containing a large SVG can trigger GPU overdraw on mobile — test compositing budget on mid-range Android devices.

Browser support

ChromeSafariFirefoxEdge
36+ 9+ 16+ 36+

SVG linearGradient and preserveAspectRatio are universally supported; no modern-CSS-only features used.

Search CodeFronts

Loading…