16 CSS Image Gallery Designs 14 / 16

CSS Scattered Photos Gallery

Six vintage postcards scattered with random CSS rotations on a cork-board surface — hover zooms them in, click brings one to front.

CSS + JS MIT licensed
Live Demo Open in tab
Open in playground

The code

<div class="ig-14" id="ig-14-board">

  <!-- Postcard 1: Rome — Colosseum golden hour -->
  <div class="ig-14__photo" style="width:190px;left:28px;top:40px;transform:rotate(-8deg);z-index:3">
    <div class="ig-14__tape"></div>
    <svg viewBox="0 0 170 130" xmlns="http://www.w3.org/2000/svg">
      <defs><linearGradient id="rmg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#ff8c40"/><stop offset="50%" stop-color="#d06018"/><stop offset="100%" stop-color="#8a3808"/></linearGradient></defs>
      <rect width="170" height="130" fill="url(#rmg)"/>
      <circle cx="85" cy="48" r="28" fill="#ffe080" opacity=".85"/>
      <!-- Colosseum ellipse form -->
      <ellipse cx="85" cy="88" rx="65" ry="36" fill="#b87030" opacity=".95"/>
      <ellipse cx="85" cy="78" rx="63" ry="34" fill="#c88040"/>
      <!-- Arcade arches -->
      <g fill="#a06028">
        <path d="M25,100 Q32,88 39,100"/><path d="M42,100 Q49,88 56,100"/>
        <path d="M59,98 Q66,86 73,98"/><path d="M76,97 Q83,85 90,97"/>
        <path d="M93,97 Q100,85 107,97"/><path d="M110,98 Q117,86 124,98"/>
        <path d="M127,100 Q134,88 141,100"/>
      </g>
      <g fill="#b87030" opacity=".6">
        <path d="M28,90 Q33,82 38,90"/><path d="M45,88 Q50,80 55,88"/>
        <path d="M62,87 Q67,79 72,87"/><path d="M79,86 Q84,78 89,86"/>
        <path d="M96,86 Q101,78 106,86"/><path d="M113,87 Q118,79 123,87"/>
        <path d="M130,90 Q135,82 140,90"/>
      </g>
      <!-- Ground shadow -->
      <path d="M20,108 Q85,98 150,108 L150,130 L20,130 Z" fill="#6a2808" opacity=".5"/>
      <!-- Pine tree -->
      <g fill="#0a2005"><rect x="148" y="88" width="5" height="42"/><ellipse cx="150" cy="84" rx="12" ry="18"/><ellipse cx="150" cy="74" rx="9" ry="12"/></g>
    </svg>
    <div class="ig-14__caption">Roma, 1994<br><small style="font-size:.8rem;color:#8a7a68">Grazie mille!</small></div>
  </div>

  <!-- Postcard 2: Paris — Eiffel Tower at dusk -->
  <div class="ig-14__photo" style="width:185px;left:195px;top:18px;transform:rotate(5deg);z-index:5">
    <div class="ig-14__tape"></div>
    <svg viewBox="0 0 165 125" xmlns="http://www.w3.org/2000/svg">
      <defs><linearGradient id="psg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#1a0a2e"/><stop offset="40%" stop-color="#5a1a60"/><stop offset="70%" stop-color="#c04050"/><stop offset="100%" stop-color="#f07858"/></linearGradient></defs>
      <rect width="165" height="125" fill="url(#psg)"/>
      <!-- Seine river dark strip -->
      <path d="M0,100 Q82,92 165,98 L165,125 L0,125 Z" fill="#1a2a4a"/>
      <!-- Eiffel tower silhouette -->
      <g fill="#0a0518">
        <!-- Leg 1 left -->
        <polygon points="55,125 62,80 75,80 70,125"/>
        <!-- Leg 2 right -->
        <polygon points="95,125 90,80 103,80 110,125"/>
        <!-- Platform 1 (lower) -->
        <rect x="52" y="80" width="61" height="7" rx="1"/>
        <!-- Body mid -->
        <polygon points="62,80 68,50 97,50 103,80"/>
        <!-- Platform 2 (upper) -->
        <rect x="60" y="50" width="45" height="6" rx="1"/>
        <!-- Tapered shaft -->
        <polygon points="68,50 73,25 92,25 97,50"/>
        <!-- Top -->
        <polygon points="73,25 80,8 85,8 92,25"/>
        <rect x="80" y="0" width="5" height="10"/>
      </g>
      <!-- Light spark at top -->
      <circle cx="82" cy="4" r="3" fill="#fff8a0" opacity=".9"/>
      <!-- City silhouette bg -->
      <g fill="#0a0518" opacity=".6">
        <rect x="0" y="95" width="40" height="30"/><rect x="125" y="90" width="40" height="35"/>
        <rect x="0" y="85" width="20" height="15"/><rect x="8" y="78" width="10" height="8"/>
      </g>
      <!-- Bridge reflection in Seine -->
      <rect x="60" y="100" width="45" height="25" fill="#0a0518" opacity=".2"/>
    </svg>
    <div class="ig-14__caption">Paris, 1997<br><small style="font-size:.8rem;color:#8a7a68">Magnifique!</small></div>
  </div>

  <!-- Postcard 3: New York — Manhattan skyline night -->
  <div class="ig-14__photo" style="width:200px;left:355px;top:30px;transform:rotate(-4deg);z-index:4">
    <div class="ig-14__tape"></div>
    <svg viewBox="0 0 180 135" xmlns="http://www.w3.org/2000/svg">
      <defs><linearGradient id="nyg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#0a0c1a"/><stop offset="50%" stop-color="#0f1228"/><stop offset="100%" stop-color="#1a1a2a"/></linearGradient></defs>
      <rect width="180" height="135" fill="url(#nyg)"/>
      <!-- Hudson River -->
      <rect x="0" y="108" width="180" height="27" fill="#080e18"/>
      <!-- Manhattan skyline silhouette -->
      <g fill="#121528">
        <rect x="0" y="60" width="16" height="75"/><rect x="14" y="45" width="14" height="90"/>
        <rect x="26" y="55" width="12" height="80"/><rect x="36" y="38" width="18" height="97"/>
        <!-- Empire State -->
        <rect x="52" y="30" width="16" height="105"/>
        <rect x="55" y="18" width="10" height="16"/>
        <rect x="57" y="8" width="6" height="12"/>
        <rect x="59" y="0" width="2" height="10"/>
        <rect x="66" y="40" width="14" height="95"/>
        <rect x="78" y="50" width="16" height="85"/>
        <!-- Chrysler -->
        <rect x="92" y="32" width="14" height="103"/>
        <path d="M92,32 Q99,16 106,32" fill="#121528"/>
        <rect x="97" y="8" width="4" height="10"/>
        <rect x="108" y="45" width="14" height="90"/>
        <rect x="120" y="38" width="18" height="97"/>
        <rect x="136" y="52" width="12" height="83"/>
        <rect x="146" y="42" width="16" height="93"/>
        <rect x="160" y="55" width="14" height="80"/>
        <rect x="172" y="60" width="8" height="75"/>
      </g>
      <!-- Window lights warm -->
      <g fill="#ffd060" opacity=".7">
        <rect x="2" y="64" width="4" height="4"/><rect x="8" y="64" width="4" height="4"/><rect x="2" y="74" width="4" height="4"/>
        <rect x="16" y="50" width="4" height="4"/><rect x="23" y="50" width="4" height="4"/><rect x="16" y="60" width="4" height="4"/>
        <rect x="38" y="42" width="5" height="4"/><rect x="46" y="42" width="5" height="4"/>
        <rect x="54" y="34" width="4" height="4"/><rect x="60" y="34" width="4" height="4"/><rect x="54" y="44" width="4" height="4"/>
        <rect x="68" y="44" width="4" height="4"/><rect x="74" y="44" width="4" height="4"/>
        <rect x="94" y="38" width="4" height="4"/><rect x="100" y="38" width="4" height="4"/>
        <rect x="110" y="48" width="4" height="4"/><rect x="116" y="48" width="5" height="4"/>
        <rect x="122" y="42" width="5" height="4"/><rect x="130" y="42" width="5" height="4"/>
        <rect x="148" y="46" width="4" height="4"/><rect x="154" y="46" width="4" height="4"/>
        <rect x="162" y="58" width="4" height="4"/><rect x="168" y="58" width="4" height="4"/>
      </g>
      <!-- Stars -->
      <g fill="#fff" opacity=".5"><circle cx="15" cy="8" r=".8"/><circle cx="50" cy="4" r="1"/><circle cx="130" cy="6" r=".9"/><circle cx="170" cy="10" r=".8"/></g>
      <!-- Water reflection -->
      <g fill="#ffd060" opacity=".2"><rect x="54" y="110" width="4" height="8"/><rect x="94" y="112" width="4" height="6"/></g>
    </svg>
    <div class="ig-14__caption">New York, 2001<br><small style="font-size:.8rem;color:#8a7a68">The city that never sleeps</small></div>
  </div>

  <!-- Postcard 4: Tokyo — Shibuya crossing day -->
  <div class="ig-14__photo" style="width:195px;left:60px;top:230px;transform:rotate(6deg);z-index:6">
    <div class="ig-14__tape"></div>
    <svg viewBox="0 0 175 130" xmlns="http://www.w3.org/2000/svg">
      <defs><linearGradient id="tjg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#c8d8e8"/><stop offset="60%" stop-color="#e8f0f8"/><stop offset="100%" stop-color="#b0c0d0"/></linearGradient></defs>
      <rect width="175" height="130" fill="url(#tjg)"/>
      <!-- Overhead: looking down at crossing -->
      <!-- Road black base -->
      <rect x="0" y="0" width="175" height="130" fill="#2a2a2a"/>
      <!-- Sidewalk corners -->
      <g fill="#aaaaaa">
        <rect x="0" y="0" width="50" height="45"/>
        <rect x="125" y="0" width="50" height="45"/>
        <rect x="0" y="85" width="50" height="45"/>
        <rect x="125" y="85" width="50" height="45"/>
      </g>
      <!-- Zebra crossing stripes horizontal -->
      <g fill="#f5f5f5" opacity=".8">
        <rect x="50" y="5" width="75" height="8" rx="1"/>
        <rect x="50" y="17" width="75" height="8" rx="1"/>
        <rect x="50" y="29" width="75" height="8" rx="1"/>
        <rect x="50" y="41" width="75" height="8" rx="1"/>
        <!-- Vertical crossing -->
        <rect x="5" y="45" width="8" height="40" rx="1"/>
        <rect x="17" y="45" width="8" height="40" rx="1"/>
        <rect x="29" y="45" width="8" height="40" rx="1"/>
        <rect x="41" y="45" width="8" height="40" rx="1"/>
        <rect x="126" y="45" width="8" height="40" rx="1"/>
        <rect x="138" y="45" width="8" height="40" rx="1"/>
        <rect x="150" y="45" width="8" height="40" rx="1"/>
        <rect x="162" y="45" width="8" height="40" rx="1"/>
        <!-- Bottom horizontal -->
        <rect x="50" y="85" width="75" height="8" rx="1"/>
        <rect x="50" y="97" width="75" height="8" rx="1"/>
        <rect x="50" y="109" width="75" height="8" rx="1"/>
        <rect x="50" y="121" width="75" height="8" rx="1"/>
      </g>
      <!-- People dots on crossings (aerial view) -->
      <g fill="#ff4444" opacity=".8"><circle cx="60" cy="22" r="3"/><circle cx="78" cy="15" r="3"/><circle cx="95" cy="28" r="3"/><circle cx="110" cy="12" r="3"/><circle cx="68" cy="38" r="3"/><circle cx="90" cy="35" r="3"/><circle cx="118" cy="30" r="3"/></g>
      <g fill="#4444ff" opacity=".7"><circle cx="65" cy="92" r="3"/><circle cx="85" cy="100" r="3"/><circle cx="105" cy="88" r="3"/><circle cx="120" cy="95" r="3"/></g>
      <g fill="#44aa44" opacity=".7"><circle cx="18" cy="55" r="3"/><circle cx="26" cy="70" r="3"/><circle cx="12" cy="75" r="3"/><circle cx="35" cy="62" r="3"/></g>
      <g fill="#ffaa00" opacity=".8"><circle cx="138" cy="58" r="3"/><circle cx="150" cy="70" r="3"/><circle cx="160" cy="55" r="3"/><circle cx="168" cy="78" r="3"/></g>
      <!-- Traffic signal circles -->
      <g fill="#ff4020" opacity=".9"><circle cx="55" cy="47" r="4"/><circle cx="120" cy="47" r="4"/><circle cx="55" cy="83" r="4"/><circle cx="120" cy="83" r="4"/></g>
      <!-- Building corner shops -->
      <g fill="#888888" opacity=".4">
        <rect x="2" y="2" width="46" height="41"/><rect x="127" y="2" width="46" height="41"/>
        <rect x="2" y="87" width="46" height="41"/><rect x="127" y="87" width="46" height="41"/>
      </g>
    </svg>
    <div class="ig-14__caption">Tokyo, 2003<br><small style="font-size:.8rem;color:#8a7a68">Shibuya scramble</small></div>
  </div>

  <!-- Postcard 5: Cairo — Pyramids + camel -->
  <div class="ig-14__photo" style="width:200px;left:255px;top:215px;transform:rotate(-5deg);z-index:2">
    <div class="ig-14__tape"></div>
    <svg viewBox="0 0 180 135" xmlns="http://www.w3.org/2000/svg">
      <defs><linearGradient id="cag" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#e8c870"/><stop offset="40%" stop-color="#d4a840"/><stop offset="100%" stop-color="#8a5010"/></linearGradient></defs>
      <rect width="180" height="135" fill="url(#cag)"/>
      <!-- Hot sky -->
      <rect width="180" height="60" fill="#e8d898" opacity=".3"/>
      <!-- Sun white-hot -->
      <circle cx="145" cy="22" r="16" fill="#fff5c0" opacity=".9"/>
      <circle cx="145" cy="22" r="12" fill="#fffce0"/>
      <!-- Desert horizon shimmer -->
      <path d="M0,80 Q45,72 90,78 Q135,72 180,76 L180,90 L0,90 Z" fill="#c09030" opacity=".4"/>
      <!-- Great Pyramid (largest) -->
      <polygon points="10,135 75,45 140,135" fill="#c89040"/>
      <polygon points="10,135 75,45 140,135" fill="#b07828" opacity=".3"/>
      <!-- Pyramid 2 (mid) -->
      <polygon points="90,135 142,58 195,135" fill="#c09030"/>
      <!-- Pyramid 3 (small, left) -->
      <polygon points="0,120 32,80 65,120" fill="#d4a840" opacity=".8"/>
      <!-- Sphinx body (silhouette) -->
      <g fill="#8a5018" transform="translate(80,108)">
        <ellipse cx="0" cy="0" rx="22" ry="10"/>
        <rect x="-12" y="-18" width="12" height="20"/>
        <ellipse cx="-6" cy="-22" rx="12" ry="14"/>
        <rect x="-14" y="-12" width="4" height="16" rx="2"/>
        <!-- Headdress -->
        <path d="M-18,-22 Q-6,-38 6,-22 L4,-16 Q-6,-28 -16,-16 Z" fill="#8a5018"/>
        <circle cx="-4" cy="-30" r="3" fill="#7a4010"/>
      </g>
      <!-- Camel with rider -->
      <g fill="#7a3a10" transform="translate(148,105)">
        <ellipse cx="0" cy="0" rx="18" ry="10"/>
        <ellipse cx="4" cy="-8" rx="10" ry="8"/>
        <path d="M-14,-4 Q-18,5 -15,14 Q-10,10 -8,4" fill="#7a3a10"/>
        <path d="M14,-2 Q18,5 16,12 Q12,8 10,2" fill="#7a3a10"/>
        <!-- Rider -->
        <ellipse cx="2" cy="-14" rx="5" ry="6" fill="#5a2808"/>
        <circle cx="2" cy="-21" r="4" fill="#c8a880"/>
        <!-- Turban -->
        <ellipse cx="2" cy="-24" rx="5" ry="3" fill="#e8c040"/>
      </g>
      <!-- Sand dune curve -->
      <path d="M0,118 Q45,108 90,115 Q135,108 180,115 L180,135 L0,135 Z" fill="#b07828" opacity=".5"/>
    </svg>
    <div class="ig-14__caption">Cairo, 2006<br><small style="font-size:.8rem;color:#8a7a68">Land of the pharaohs</small></div>
  </div>

  <!-- Postcard 6: Istanbul — Blue Mosque at dusk -->
  <div class="ig-14__photo" style="width:188px;left:460px;top:225px;transform:rotate(7deg);z-index:7">
    <div class="ig-14__tape"></div>
    <svg viewBox="0 0 168 128" xmlns="http://www.w3.org/2000/svg">
      <defs><linearGradient id="isg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#1a0830"/><stop offset="35%" stop-color="#4a1850"/><stop offset="65%" stop-color="#9a3858"/><stop offset="100%" stop-color="#d06858"/></linearGradient><filter id="isf"><feGaussianBlur stdDeviation="3"/></filter></defs>
      <rect width="168" height="128" fill="url(#isg)"/>
      <!-- Crescent moon -->
      <circle cx="140" cy="18" r="12" fill="#fff8d0" opacity=".9"/>
      <circle cx="145" cy="15" r="9" fill="#4a1850"/>
      <circle cx="143" cy="13" r="7" fill="#fff8d0" opacity=".9"/>
      <!-- Blue Mosque silhouette -->
      <g fill="#0a0518">
        <!-- Main dome -->
        <ellipse cx="84" cy="58" rx="30" ry="24"/>
        <!-- Semi-domes cascading -->
        <ellipse cx="84" cy="72" rx="36" ry="14"/>
        <ellipse cx="57" cy="72" rx="16" ry="10"/><ellipse cx="111" cy="72" rx="16" ry="10"/>
        <ellipse cx="84" cy="62" rx="28" ry="16"/>
        <!-- Main body -->
        <rect x="48" y="72" width="72" height="56"/>
        <!-- 6 minarets -->
        <rect x="30" y="30" width="7" height="72"/><circle cx="33" cy="28" r="5"/>
        <rect x="41" y="40" width="7" height="60"/><circle cx="44" cy="38" r="4.5"/>
        <rect x="120" y="40" width="7" height="60"/><circle cx="123" cy="38" r="4.5"/>
        <rect x="131" y="30" width="7" height="72"/><circle cx="134" cy="28" r="5"/>
        <!-- Front minarets -->
        <rect x="52" y="45" width="6" height="45"/><circle cx="55" cy="43" r="4"/>
        <rect x="110" y="45" width="6" height="45"/><circle cx="113" cy="43" r="4"/>
        <!-- Minaret tips -->
        <g fill="#0a0518">
          <polygon points="30,28 33,18 36,28"/><polygon points="131,28 134,18 137,28"/>
          <polygon points="41,38 44,29 47,38"/><polygon points="120,38 123,29 126,38"/>
          <polygon points="52,43 55,34 58,43"/><polygon points="110,43 113,34 116,43"/>
        </g>
        <!-- Arched windows -->
        <g fill="#2a1848" opacity=".7">
          <path d="M55,80 Q62,70 69,80 L69,95 L55,95 Z"/>
          <path d="M73,80 Q80,70 87,80 L87,95 L73,95 Z"/>
          <path d="M91,80 Q98,70 105,80 L105,95 L91,95 Z"/>
          <path d="M109,80 Q116,70 123,80 L123,95 L109,95 Z"/>
        </g>
      </g>
      <!-- Reflection in Bosphorus water -->
      <path d="M0,115 Q84,108 168,113 L168,128 L0,128 Z" fill="#1a0828" opacity=".6"/>
      <g fill="#0a0518" opacity=".3" transform="translate(0,235) scale(1,-1)">
        <ellipse cx="84" cy="58" rx="25" ry="18"/>
        <rect x="52" y="72" width="64" height="30"/>
      </g>
      <!-- Stars emerging -->
      <g fill="#fff" opacity=".6"><circle cx="20" cy="10" r=".9"/><circle cx="60" cy="5" r="1"/><circle cx="100" cy="8" r=".8"/></g>
      <!-- Warm ground lights -->
      <ellipse cx="84" cy="125" rx="80" ry="8" fill="#c04828" opacity=".2" filter="url(#isf)"/>
    </svg>
    <div class="ig-14__caption">Istanbul, 2009<br><small style="font-size:.8rem;color:#8a7a68">Where East meets West</small></div>
  </div>

</div>
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
body{background:radial-gradient(ellipse at 30% 70%,#2a1a0a,#0a0805 70%);min-height:100vh;display:flex;align-items:center;justify-content:center;padding:2rem;font-family:'DM Sans',sans-serif}
.ig-14{position:relative;width:min(680px,100%);height:520px}
.ig-14__photo{position:absolute;background:#f5f0e8;padding:10px 10px 42px;box-shadow:0 8px 32px rgba(0,0,0,.55),0 2px 6px rgba(0,0,0,.3);border-radius:2px;cursor:pointer;transition:transform .35s cubic-bezier(.34,1.56,.64,1),box-shadow .3s ease,z-index 0s;will-change:transform}
.ig-14__photo:hover{box-shadow:0 24px 60px rgba(0,0,0,.65);z-index:10}
.ig-14__photo svg{display:block;width:100%;height:auto}
.ig-14__caption{text-align:center;font-family:'Caveat',cursive;font-size:1rem;color:#4a3a28;margin-top:.4rem;line-height:1.2}
.ig-14__tape{position:absolute;top:-12px;left:50%;transform:translateX(-50%);width:38px;height:16px;background:rgba(255,240,200,.55);border-radius:2px}
@media(prefers-reduced-motion:reduce){.ig-14__photo{transition:none}}
(function(){
  const root=document.getElementById('ig-14-board');
  if(!root)return;
  const photos=root.querySelectorAll('.ig-14__photo');
  photos.forEach((p,i)=>{
    p.addEventListener('mouseenter',()=>{p.style.transform=p.style.transform.replace(/scale\([^)]*\)/,'')+'scale(1.06)'});
    p.addEventListener('mouseleave',()=>{p.style.transform=p.style.transform.replace(/scale\([^)]*\)/,'')});
    p.addEventListener('click',()=>{
      photos.forEach((x,j)=>{
        const base=parseInt(x.style.zIndex)||j+1;
        x.style.zIndex=j===i?20:base>10?base-1:base;
      });
    });
  });
}());

How this works

Each photo is absolutely positioned within a fixed-height board with inline top, left, and transform: rotate(Ndeg) values set in the HTML. JavaScript adds a subtle scale hover effect and manages z-index on click, bringing the clicked photo to the top layer. The polaroid-style frame is pure CSS: padding: 10px 10px 42px with a cream background and multilayer box-shadow.

Tape strips use absolutely-positioned .ig-14__tape divs with a translucent background and slight transparency. The overall cork-board background uses a radial gradient from warm brown tones. All photo rotations and positions are declared inline so each card can be independently designed without JavaScript-generated values.

Customize

  • Add more photos by duplicating a .ig-14__photo div and adjusting its left, top, and transform: rotate() inline values.
  • Change the board texture by editing the body background: radial-gradient(ellipse at 30% 70%, #2a1a0a, #0a0805 70%) — try a lighter linen color for a bright mood board.
  • Add a dog-eared corner by using a ::before pseudo-element with a triangle clip-path on a slightly different shade of the card background.
  • Make photos draggable by adding a mousedown event listener that updates the left and top CSS properties as the mouse moves.
  • Vary shadow depth per card by adding a data-depth attribute and using JS to set the box-shadow blur radius proportionally.

Watch out for

  • Absolute positioning with fixed top/left values overflows on small screens — either set overflow: hidden on the board or use responsive percentage values instead of pixels.
  • Random rotation values declared in HTML are not truly random — for dynamic randomisation on load, use JS to generate and inject rotate values into inline styles.
  • Stacking context conflicts: when JS sets a card's z-index to 20, parent elements with their own z-index or transform can create new stacking contexts that cap the child z-index.

Browser support

ChromeSafariFirefoxEdge
4+ 3.1+ 2+ 4+

Absolute positioning and CSS transforms universally supported; no modern-only features used beyond box-shadow and border-radius.

Search CodeFronts

Loading…