16 CSS Image Gallery Designs 05 / 16

CSS Tilt Parallax Image Cards

Three architecture cards with a CSS 3D tilt effect on hover plus a shimmer light-sweep across the face — pure CSS, no JavaScript.

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

The code

<div class="ig-05__grid">
  <!-- 1: Burj Khalifa at night -->
  <div class="ig-05__wrap">
    <div class="ig-05__card" style="--accent:#00aaff">
      <div class="ig-05__img">
        <svg viewBox="0 0 200 267" xmlns="http://www.w3.org/2000/svg">
          <defs><linearGradient id="bkg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#010818"/><stop offset="60%" stop-color="#040c22"/><stop offset="100%" stop-color="#060d2a"/></linearGradient><filter id="bkf"><feGaussianBlur stdDeviation="4"/></filter></defs>
          <rect width="200" height="267" fill="url(#bkg)"/>
          <!-- City skyline bg -->
          <g fill="#080e1a"><rect x="0" y="160" width="30" height="107"/><rect x="28" y="140" width="22" height="127"/><rect x="145" y="145" width="28" height="122"/><rect x="170" y="155" width="30" height="112"/></g>
          <!-- Burj Khalifa -->
          <g fill="#1a2840">
            <polygon points="82,267 86,50 100,10 114,50 118,267"/>
            <polygon points="86,200 92,150 100,120 108,150 114,200"/>
          </g>
          <g fill="#2a3850" opacity=".7">
            <polygon points="90,267 93,100 100,30 107,100 110,267"/>
          </g>
          <!-- Window lights -->
          <g fill="#88aacc" opacity=".8">
            <rect x="92" y="220" width="4" height="3"/><rect x="98" y="220" width="4" height="3"/>
            <rect x="91" y="210" width="4" height="3"/><rect x="97" y="210" width="4" height="3"/><rect x="103" y="210" width="4" height="3"/>
            <rect x="90" y="200" width="4" height="3"/><rect x="96" y="200" width="4" height="3"/><rect x="102" y="200" width="4" height="3"/>
            <rect x="90" y="190" width="4" height="3"/><rect x="96" y="190" width="4" height="3"/><rect x="102" y="190" width="4" height="3"/>
            <rect x="91" y="180" width="3.5" height="2.5"/><rect x="97" y="180" width="3.5" height="2.5"/><rect x="102" y="180" width="3.5" height="2.5"/>
            <rect x="92" y="170" width="3.5" height="2.5"/><rect x="98" y="170" width="3.5" height="2.5"/>
            <rect x="93" y="160" width="3" height="2.5"/><rect x="99" y="160" width="3" height="2.5"/>
            <rect x="94" y="150" width="3" height="2"/><rect x="100" y="150" width="3" height="2"/>
          </g>
          <!-- Antenna glow -->
          <circle cx="100" cy="10" r="4" fill="#00aaff" opacity=".9" filter="url(#bkf)"/>
          <circle cx="100" cy="10" r="2" fill="#88ccff"/>
          <!-- Stars -->
          <g fill="#fff" opacity=".6"><circle cx="15" cy="20" r=".9"/><circle cx="50" cy="12" r="1"/><circle cx="160" cy="18" r=".9"/><circle cx="185" cy="8" r="1.1"/><circle cx="30" cy="45" r=".7"/><circle cx="175" cy="38" r=".8"/></g>
          <!-- Ground glow -->
          <ellipse cx="100" cy="265" rx="100" ry="12" fill="#0044aa" opacity=".4" filter="url(#bkf)"/>
        </svg>
      </div>
      <span class="ig-05__badge">Sky</span>
      <div class="ig-05__info"><strong>Burj Khalifa</strong><span>Dubai, UAE · 828m</span></div>
    </div>
  </div>
  <!-- 2: Colosseum golden hour -->
  <div class="ig-05__wrap">
    <div class="ig-05__card" style="--accent:#ff8c00">
      <div class="ig-05__img">
        <svg viewBox="0 0 200 267" xmlns="http://www.w3.org/2000/svg">
          <defs><linearGradient id="cog" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#ff9840"/><stop offset="40%" stop-color="#e07020"/><stop offset="100%" stop-color="#a04810"/></linearGradient><filter id="cof"><feGaussianBlur stdDeviation="3"/></filter></defs>
          <rect width="200" height="267" fill="url(#cog)"/>
          <circle cx="100" cy="80" r="40" fill="#ffe080" opacity=".8"/>
          <circle cx="100" cy="80" r="32" fill="#fff4a0" opacity=".9"/>
          <ellipse cx="100" cy="110" rx="130" ry="30" fill="#ffaa40" opacity=".4" filter="url(#cof)"/>
          <!-- Colosseum elliptical form -->
          <ellipse cx="100" cy="190" rx="90" ry="50" fill="#c89050" opacity=".95"/>
          <ellipse cx="100" cy="175" rx="88" ry="48" fill="#d8a060"/>
          <ellipse cx="100" cy="170" rx="82" ry="44" fill="#c09050"/>
          <!-- Arcade arches - bottom tier -->
          <g fill="#a07040">
            <path d="M15,215 Q25,200 35,215"/><path d="M38,215 Q48,200 58,215"/>
            <path d="M62,213 Q72,198 82,213"/><path d="M86,212 Q96,197 106,212"/>
            <path d="M110,212 Q120,197 130,212"/><path d="M134,213 Q144,198 154,213"/>
            <path d="M158,215 Q168,200 178,215"/>
          </g>
          <!-- Second tier arches -->
          <g fill="#b08050" opacity=".7">
            <path d="M20,200 Q28,188 36,200"/><path d="M44,198 Q52,186 60,198"/>
            <path d="M68,197 Q76,185 84,197"/><path d="M92,196 Q100,184 108,196"/>
            <path d="M116,196 Q124,184 132,196"/><path d="M140,198 Q148,186 156,198"/>
            <path d="M162,200 Q170,188 178,200"/>
          </g>
          <!-- Broken section -->
          <polygon points="165,170 190,160 195,185 188,220 160,225" fill="#a07040" opacity=".6"/>
          <!-- Rubble at base -->
          <g fill="#7a5030" opacity=".5">
            <ellipse cx="40" cy="235" rx="15" ry="6"/><ellipse cx="160" cy="238" rx="12" ry="5"/>
            <ellipse cx="100" cy="240" rx="10" ry="4"/>
          </g>
          <!-- Ground shadow -->
          <path d="M10,240 Q100,228 190,240 L200,267 L0,267 Z" fill="#5a2808" opacity=".5"/>
        </svg>
      </div>
      <span class="ig-05__badge">Ancient</span>
      <div class="ig-05__info"><strong>Colosseum</strong><span>Rome, Italy · 70 CE</span></div>
    </div>
  </div>
  <!-- 3: Machu Picchu misty -->
  <div class="ig-05__wrap">
    <div class="ig-05__card" style="--accent:#00cc88">
      <div class="ig-05__img">
        <svg viewBox="0 0 200 267" xmlns="http://www.w3.org/2000/svg">
          <defs><linearGradient id="mpg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#2a4a2a"/><stop offset="40%" stop-color="#3a6a3a"/><stop offset="70%" stop-color="#4a7a4a"/><stop offset="100%" stop-color="#3a5a2a"/></linearGradient><filter id="mpf"><feGaussianBlur stdDeviation="5"/></filter><filter id="mpf2"><feGaussianBlur stdDeviation="2"/></filter></defs>
          <rect width="200" height="267" fill="url(#mpg)"/>
          <!-- Misty mountains bg -->
          <g filter="url(#mpf)">
            <polygon points="0,80 50,30 100,65 150,25 200,55 200,120 0,120" fill="#3a5a2a" opacity=".7"/>
            <polygon points="50,267 90,60 130,267" fill="#4a6a30" opacity=".5"/>
          </g>
          <!-- Huayna Picchu peak -->
          <polygon points="130,0 155,60 180,0" fill="#2a3a20" opacity=".9"/>
          <polygon points="135,0 155,55 175,0" fill="#3a4a28" opacity=".7"/>
          <!-- Mist layers -->
          <rect x="0" y="70" width="200" height="30" fill="#c8d8c0" opacity=".25" filter="url(#mpf)"/>
          <rect x="0" y="90" width="200" height="25" fill="#d0dcc8" opacity=".2" filter="url(#mpf)"/>
          <!-- Terraces -->
          <g fill="#4a6a30">
            <path d="M0,145 Q50,138 100,143 Q150,138 200,145 L200,155 L0,155 Z"/>
            <path d="M0,158 Q50,151 100,156 Q150,151 200,158 L200,168 L0,168 Z"/>
            <path d="M0,171 Q50,164 100,169 Q150,164 200,171 L200,181 L0,181 Z"/>
            <path d="M0,184 Q50,177 100,182 Q150,177 200,184 L200,194 L0,194 Z"/>
            <path d="M0,197 Q50,190 100,195 Q150,190 200,197 L200,207 L0,207 Z"/>
          </g>
          <!-- Stone walls -->
          <g stroke="#2a3a18" stroke-width="1" fill="none" opacity=".5">
            <line x1="0" y1="155" x2="200" y2="155"/><line x1="0" y1="168" x2="200" y2="168"/>
            <line x1="0" y1="181" x2="200" y2="181"/><line x1="0" y1="194" x2="200" y2="194"/>
          </g>
          <!-- Inca structures -->
          <g fill="#5a7840">
            <rect x="40" y="125" width="30" height="22"/><polygon points="35,125 55,112 75,125" fill="#4a6830"/>
            <rect x="80" y="122" width="28" height="24"/><polygon points="76,122 94,109 112,122" fill="#4a6830"/>
            <rect x="118" y="128" width="26" height="20"/><polygon points="114,128 131,116 148,128" fill="#4a6830"/>
          </g>
          <!-- Llama silhouette -->
          <g fill="#2a3a18" transform="translate(155,185)">
            <ellipse cx="0" cy="0" rx="12" ry="7"/>
            <rect x="-4" y="6" width="3" height="10" rx="2"/>
            <rect x="3" y="6" width="3" height="10" rx="2"/>
            <ellipse cx="-10" cy="-4" rx="5" ry="7"/>
            <ellipse cx="-13" cy="-10" rx="3" ry="4"/>
          </g>
          <!-- Green vegetation on terraces -->
          <g fill="#2a5a14" opacity=".6">
            <ellipse cx="20" cy="148" rx="12" ry="5"/><ellipse cx="80" cy="145" rx="10" ry="4"/>
            <ellipse cx="150" cy="148" rx="11" ry="4.5"/>
          </g>
        </svg>
      </div>
      <span class="ig-05__badge">Wonder</span>
      <div class="ig-05__info"><strong>Machu Picchu</strong><span>Cusco, Peru · 2430m</span></div>
    </div>
  </div>
</div>
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
body{background:#080c14;font-family:'Space Grotesk',sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2.5rem}
.ig-05__grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:1.5rem;width:100%;max-width:720px}
.ig-05__wrap{perspective:700px}
.ig-05__card{border-radius:18px;overflow:hidden;position:relative;transform-style:preserve-3d;transition:transform .45s cubic-bezier(.25,.46,.45,.94),box-shadow .45s ease;box-shadow:0 8px 28px rgba(0,0,0,.5);cursor:pointer}
.ig-05__card::after{content:'';position:absolute;inset:0;background:linear-gradient(135deg,transparent 40%,rgba(255,255,255,.16) 50%,transparent 60%);transform:translateX(-120%);transition:transform .5s ease;pointer-events:none}
.ig-05__wrap:hover .ig-05__card{transform:rotateX(8deg) rotateY(-8deg) scale(1.04);box-shadow:0 28px 64px rgba(0,0,0,.6)}
.ig-05__wrap:hover .ig-05__card::after{transform:translateX(120%)}
.ig-05__img{width:100%;aspect-ratio:3/4;display:block}
.ig-05__img svg{width:100%;height:100%;display:block}
.ig-05__badge{position:absolute;top:1.1rem;right:1.1rem;transform:translateZ(28px);background:var(--accent);color:#fff;font-size:.65rem;font-weight:700;letter-spacing:.1em;padding:.22rem .55rem;border-radius:6px;box-shadow:0 4px 14px rgba(0,0,0,.35);text-transform:uppercase}
.ig-05__info{padding:1rem;background:#0f1420}
.ig-05__info strong{color:#fff;font-size:.9rem;font-weight:700;display:block;margin-bottom:.2rem}
.ig-05__info span{color:rgba(255,255,255,.45);font-size:.75rem}
@media(prefers-reduced-motion:reduce){.ig-05__card,.ig-05__card::after{transition:none}}

How this works

Each card is wrapped in a .ig-05__wrap with perspective: 700px. On :hover, the inner card applies transform: rotateX(8deg) rotateY(-8deg) scale(1.04), creating the tilt illusion purely in CSS. The transition uses cubic-bezier(.25,.46,.45,.94) for a smooth ease-out that does not overshoot.

The shimmer is a ::after pseudo-element with a diagonal linear-gradient from transparent to a semi-opaque white and back. On idle, it sits at translateX(-120%) (off-screen left); on hover a transition moves it to translateX(120%) — sweeping across the card face once. No keyframe animation is needed: a single transition handles the one-shot sweep.

Customize

  • Increase the tilt depth by changing rotateX(8deg) rotateY(-8deg) to larger values like 12deg — values above 15deg tend to look distorted.
  • Flip the tilt direction by swapping negative/positive: rotateX(-8deg) rotateY(8deg) tilts the opposite corner toward the viewer.
  • Change the shimmer color from white to a gold tone: replace rgba(255,255,255,.16) with rgba(255,200,80,.22) for a warm metallic sweep.
  • Add a colored drop shadow on hover by extending the box-shadow rule with a color-tinted shadow like 0 28px 64px rgba(0,170,255,.25).
  • Control accent color per card via the --accent CSS custom property on each card element — used for the badge background.

Watch out for

  • CSS-only tilt is fixed at a single angle regardless of mouse position — for a mouse-tracking tilt, JavaScript is required to calculate pointer offsets and set inline transforms.
  • transform-style: preserve-3d on the parent and backface-visibility: hidden are needed for child elements to participate in the 3D space; forgetting these causes the tilt to flatten.
  • Combining overflow: hidden with 3D transforms can cause clipping artefacts on Safari — apply overflow: hidden to an inner wrapper rather than the element receiving the 3D transform.

Browser support

ChromeSafariFirefoxEdge
36+ 9+ 10+ 36+

3D CSS transforms and perspective are widely supported; backdrop-filter for glass badges needs Chrome 76+, Safari 9+.

Search CodeFronts

Loading…