16 CSS Image Gallery Designs 01 / 16

CSS Masonry Image Gallery

A three-column CSS-only masonry grid where each card reveals a rich SVG-illustrated scene on hover, with a staggered caption fade.

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

The code

<div class="ig-01">
  <div class="ig-01__grid">

    <!-- TALL 1: Thunderstorm over savanna -->
    <figure class="ig-01__card ig-01__card--tall">
      <svg class="ig-01__scene" viewBox="0 0 300 400" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <linearGradient id="sky1" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stop-color="#1a0a2e"/>
            <stop offset="40%" stop-color="#3d1a5c"/>
            <stop offset="70%" stop-color="#7a3a20"/>
            <stop offset="100%" stop-color="#c45a10"/>
          </linearGradient>
          <filter id="lglow"><feGaussianBlur stdDeviation="6"/></filter>
          <filter id="lglow2"><feGaussianBlur stdDeviation="2"/></filter>
        </defs>
        <rect width="300" height="400" fill="url(#sky1)"/>
        <!-- Storm clouds -->
        <ellipse cx="150" cy="80" rx="160" ry="70" fill="#1a1020" opacity=".95"/>
        <ellipse cx="80" cy="60" rx="100" ry="55" fill="#12080e" opacity=".9"/>
        <ellipse cx="230" cy="70" rx="90" ry="50" fill="#150c18" opacity=".85"/>
        <ellipse cx="150" cy="50" rx="130" ry="40" fill="#0d0810" opacity=".9"/>
        <!-- Lightning bolt -->
        <g filter="url(#lglow)">
          <polyline points="170,90 155,155 170,155 148,230" stroke="#ffffa0" stroke-width="6" fill="none" opacity=".9"/>
        </g>
        <polyline points="170,90 155,155 170,155 148,230" stroke="#fff" stroke-width="2" fill="none"/>
        <!-- Second smaller lightning -->
        <g filter="url(#lglow2)" opacity=".6">
          <polyline points="230,100 220,145 228,145 215,185" stroke="#c0d8ff" stroke-width="3" fill="none"/>
        </g>
        <!-- Horizon glow -->
        <ellipse cx="150" cy="290" rx="200" ry="45" fill="#c45a10" opacity=".4" filter="url(#lglow2)"/>
        <!-- Savanna silhouette -->
        <rect x="0" y="295" width="300" height="105" fill="#0d0804"/>
        <!-- Acacia trees -->
        <g fill="#0a0602">
          <rect x="25" y="260" width="5" height="40"/>
          <ellipse cx="27" cy="255" rx="22" ry="8"/>
          <rect x="80" y="250" width="6" height="50"/>
          <ellipse cx="83" cy="244" rx="28" ry="10"/>
          <rect x="160" y="258" width="5" height="42"/>
          <ellipse cx="162" cy="252" rx="20" ry="7"/>
          <rect x="230" y="255" width="6" height="45"/>
          <ellipse cx="233" cy="248" rx="25" ry="9"/>
          <rect x="270" y="265" width="4" height="35"/>
          <ellipse cx="272" cy="260" rx="16" ry="6"/>
        </g>
        <!-- Animals silhouettes (wildebeest) -->
        <g fill="#080502" transform="translate(40,285)">
          <ellipse cx="0" cy="5" rx="8" ry="4"/><rect x="-2" y="7" width="2" height="6"/><rect x="3" y="7" width="2" height="6"/>
          <ellipse cx="20" cy="5" rx="7" ry="3.5"/><rect x="18" y="7" width="2" height="5"/><rect x="23" y="7" width="2" height="5"/>
          <ellipse cx="38" cy="4" rx="9" ry="4"/><rect x="36" y="7" width="2" height="6"/><rect x="41" y="7" width="2" height="6"/>
          <ellipse cx="140" cy="5" rx="7" ry="3.5"/><rect x="138" y="7" width="2" height="5"/><rect x="143" y="7" width="2" height="5"/>
          <ellipse cx="158" cy="4" rx="8" ry="4"/><rect x="156" y="7" width="2" height="6"/><rect x="161" y="7" width="2" height="6"/>
        </g>
      </svg>
      <figcaption class="ig-01__caption"><span>Storm Over Serengeti</span><small>Wildlife · Tanzania</small></figcaption>
    </figure>

    <!-- SHORT 1: Cherry blossoms -->
    <figure class="ig-01__card ig-01__card--short">
      <svg class="ig-01__scene" viewBox="0 0 300 225" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <linearGradient id="sky2" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stop-color="#a8d8f0"/>
            <stop offset="50%" stop-color="#e8c8d8"/>
            <stop offset="100%" stop-color="#f5e0e8"/>
          </linearGradient>
          <filter id="psoft"><feGaussianBlur stdDeviation="1.5"/></filter>
        </defs>
        <rect width="300" height="225" fill="url(#sky2)"/>
        <!-- Sun disc -->
        <circle cx="200" cy="55" r="28" fill="#fff0c0" opacity=".8"/>
        <circle cx="200" cy="55" r="20" fill="#fff8d0" opacity=".9"/>
        <!-- Tree trunks -->
        <g fill="#3d1a0a">
          <rect x="55" y="90" width="14" height="135" rx="5"/>
          <rect x="180" y="100" width="11" height="125" rx="4"/>
          <rect x="240" y="110" width="9" height="115" rx="3"/>
        </g>
        <!-- Branches -->
        <g stroke="#3d1a0a" stroke-width="5" fill="none" stroke-linecap="round">
          <path d="M62,120 Q30,90 10,70"/>
          <path d="M62,120 Q90,80 120,65"/>
          <path d="M62,130 Q85,120 110,115"/>
          <path d="M186,125 Q160,95 140,80"/>
          <path d="M186,125 Q210,100 240,90"/>
        </g>
        <!-- Blossom clusters -->
        <g filter="url(#psoft)">
          <!-- Tree 1 blossoms -->
          <circle cx="10" cy="70" r="22" fill="#f0b0c8" opacity=".85"/>
          <circle cx="40" cy="55" r="28" fill="#f8c8d8" opacity=".9"/>
          <circle cx="80" cy="50" r="30" fill="#fad4e0" opacity=".88"/>
          <circle cx="115" cy="62" r="24" fill="#f0b0c8" opacity=".82"/>
          <circle cx="62" cy="85" r="20" fill="#f8c8d8" opacity=".75"/>
          <circle cx="108" cy="110" r="18" fill="#fad4e0" opacity=".7"/>
          <!-- Tree 2 -->
          <circle cx="140" cy="78" r="20" fill="#f0b0c8" opacity=".8"/>
          <circle cx="168" cy="68" r="25" fill="#f8c8d8" opacity=".85"/>
          <circle cx="200" cy="65" r="22" fill="#fad4e0" opacity=".8"/>
          <circle cx="225" cy="75" r="20" fill="#f0b0c8" opacity=".78"/>
          <!-- Tree 3 -->
          <circle cx="245" cy="90" r="18" fill="#f8c8d8" opacity=".8"/>
          <circle cx="270" cy="82" r="22" fill="#fad4e0" opacity=".82"/>
          <circle cx="290" cy="95" r="16" fill="#f0b0c8" opacity=".75"/>
        </g>
        <!-- Falling petals -->
        <g fill="#f8c8d8" opacity=".7">
          <ellipse cx="50" cy="140" rx="4" ry="2.5" transform="rotate(20,50,140)"/>
          <ellipse cx="130" cy="155" rx="3.5" ry="2" transform="rotate(-15,130,155)"/>
          <ellipse cx="180" cy="135" rx="4" ry="2.5" transform="rotate(35,180,135)"/>
          <ellipse cx="260" cy="150" rx="3" ry="2" transform="rotate(-25,260,150)"/>
          <ellipse cx="95" cy="170" rx="4" ry="2.5" transform="rotate(10,95,170)"/>
          <ellipse cx="220" cy="165" rx="3.5" ry="2" transform="rotate(40,220,165)"/>
        </g>
        <!-- Ground / path -->
        <ellipse cx="150" cy="225" rx="180" ry="30" fill="#e8c8d0" opacity=".5"/>
        <rect x="0" y="200" width="300" height="25" fill="#d4b0b8" opacity=".4"/>
        <!-- Petals on ground -->
        <g fill="#f0c0d0" opacity=".6">
          <ellipse cx="30" cy="215" rx="5" ry="2.5" transform="rotate(15,30,215)"/>
          <ellipse cx="100" cy="218" rx="4" ry="2" transform="rotate(-20,100,218)"/>
          <ellipse cx="170" cy="212" rx="5.5" ry="2.5" transform="rotate(30,170,212)"/>
          <ellipse cx="250" cy="216" rx="4" ry="2" transform="rotate(-10,250,216)"/>
        </g>
      </svg>
      <figcaption class="ig-01__caption"><span>Hanami Season</span><small>Botanical · Kyoto</small></figcaption>
    </figure>

    <!-- MID 1: Deep cave / crystal -->
    <figure class="ig-01__card ig-01__card--mid">
      <svg class="ig-01__scene" viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <radialGradient id="cavebg" cx="50%" cy="40%" r="60%">
            <stop offset="0%" stop-color="#1a2a5e"/>
            <stop offset="50%" stop-color="#0a1030"/>
            <stop offset="100%" stop-color="#050810"/>
          </radialGradient>
          <filter id="cglow"><feGaussianBlur stdDeviation="5"/></filter>
          <filter id="cglow2"><feGaussianBlur stdDeviation="2"/></filter>
        </defs>
        <rect width="300" height="300" fill="url(#cavebg)"/>
        <!-- Cave opening glow -->
        <ellipse cx="150" cy="130" rx="80" ry="60" fill="#2040a0" opacity=".3" filter="url(#cglow)"/>
        <!-- Crystal formations -->
        <g opacity=".9">
          <!-- Left cluster -->
          <polygon points="30,300 45,200 55,300" fill="#4060c0" opacity=".7"/>
          <polygon points="50,300 62,210 72,300" fill="#5070d0" opacity=".8"/>
          <polygon points="20,300 38,220 48,300" fill="#3050b0" opacity=".6"/>
          <polygon points="65,300 75,230 84,300" fill="#6080e0" opacity=".75"/>
          <!-- Right cluster -->
          <polygon points="220,300 232,195 245,300" fill="#4060c0" opacity=".7"/>
          <polygon points="240,300 250,215 260,300" fill="#5575d5" opacity=".8"/>
          <polygon points="255,300 265,225 278,300" fill="#3a55b5" opacity=".65"/>
          <polygon points="270,300 278,240 286,300" fill="#6080e0" opacity=".7"/>
          <!-- Center top crystals -->
          <polygon points="120,300 130,140 142,300" fill="#5878e0" opacity=".6"/>
          <polygon points="145,300 155,120 168,300" fill="#7090f0" opacity=".7"/>
          <polygon points="168,300 178,145 188,300" fill="#5070d0" opacity=".6"/>
        </g>
        <!-- Crystal glow highlights -->
        <g filter="url(#cglow2)">
          <line x1="155" y1="120" x2="155" y2="300" stroke="#a0c0ff" stroke-width="2" opacity=".4"/>
          <line x1="130" y1="140" x2="130" y2="300" stroke="#80a0ff" stroke-width="1.5" opacity=".35"/>
          <line x1="178" y1="145" x2="178" y2="300" stroke="#90b0ff" stroke-width="1.5" opacity=".35"/>
        </g>
        <!-- Cave ceiling -->
        <path d="M0,0 Q50,40 80,20 Q110,50 150,30 Q190,55 220,25 Q260,45 300,20 L300,0 Z" fill="#050810"/>
        <!-- Stalactites -->
        <g fill="#0a1225">
          <polygon points="30,20 40,80 50,20"/>
          <polygon points="80,15 88,65 96,15"/>
          <polygon points="130,20 138,70 146,20"/>
          <polygon points="165,18 172,60 180,18"/>
          <polygon points="210,22 218,72 226,22"/>
          <polygon points="255,16 262,58 270,16"/>
        </g>
        <!-- Glow light source at center -->
        <circle cx="152" cy="155" r="25" fill="#3060c0" opacity=".35" filter="url(#cglow)"/>
        <circle cx="152" cy="155" r="8" fill="#8090f0" opacity=".5"/>
        <!-- Reflection pool at bottom -->
        <ellipse cx="150" cy="285" rx="100" ry="15" fill="#3060c0" opacity=".25"/>
        <ellipse cx="150" cy="285" rx="60" ry="8" fill="#5080e0" opacity=".2"/>
      </svg>
      <figcaption class="ig-01__caption"><span>Crystal Cave</span><small>Geology · Iceland</small></figcaption>
    </figure>

    <!-- TALL 2: Lavender field sunset -->
    <figure class="ig-01__card ig-01__card--tall">
      <svg class="ig-01__scene" viewBox="0 0 300 400" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <linearGradient id="sky3" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stop-color="#ff6b35"/>
            <stop offset="30%" stop-color="#f7a44a"/>
            <stop offset="55%" stop-color="#ffd580"/>
            <stop offset="75%" stop-color="#a85cb8"/>
            <stop offset="100%" stop-color="#6a3a8a"/>
          </linearGradient>
          <filter id="sf"><feGaussianBlur stdDeviation="4"/></filter>
        </defs>
        <rect width="300" height="400" fill="url(#sky3)"/>
        <!-- Sun -->
        <circle cx="150" cy="155" r="35" fill="#fff5c0" opacity=".9"/>
        <circle cx="150" cy="155" r="28" fill="#ffec90" opacity=".95"/>
        <!-- Sun rays -->
        <g stroke="#ffe070" stroke-width="2" opacity=".3" filter="url(#sf)">
          <line x1="150" y1="100" x2="150" y2="75"/>
          <line x1="195" y1="115" x2="212" y2="98"/>
          <line x1="210" y1="155" x2="235" y2="155"/>
          <line x1="195" y1="195" x2="212" y2="212"/>
          <line x1="150" y1="210" x2="150" y2="235"/>
          <line x1="105" y1="195" x2="88" y2="212"/>
          <line x1="90" y1="155" x2="65" y2="155"/>
          <line x1="105" y1="115" x2="88" y2="98"/>
        </g>
        <!-- Clouds -->
        <g opacity=".6" filter="url(#sf)">
          <ellipse cx="60" cy="90" rx="50" ry="18" fill="#ffcca0"/>
          <ellipse cx="250" cy="105" rx="55" ry="16" fill="#ffb880"/>
          <ellipse cx="160" cy="75" rx="40" ry="14" fill="#ffd0a0"/>
        </g>
        <!-- Distant hills -->
        <path d="M0,240 Q75,210 150,225 Q225,210 300,235 L300,400 L0,400 Z" fill="#5a2870" opacity=".6"/>
        <path d="M0,260 Q80,240 150,250 Q220,240 300,258 L300,400 L0,400 Z" fill="#4a1f60" opacity=".7"/>
        <!-- Lavender rows — perspective converging lines -->
        <g stroke="none">
          <!-- Row fills converging to center vanishing point -->
          <path d="M0,290 L300,270 L300,275 L0,295 Z" fill="#9b59b6" opacity=".9"/>
          <path d="M0,300 L300,278 L300,284 L0,307 Z" fill="#8e44ad" opacity=".85"/>
          <path d="M0,312 L300,287 L300,294 L0,320 Z" fill="#7d3c98" opacity=".88"/>
          <path d="M0,324 L300,297 L300,305 L0,334 Z" fill="#9b59b6" opacity=".9"/>
          <path d="M0,338 L300,308 L300,317 L0,350 Z" fill="#8e44ad" opacity=".88"/>
          <path d="M0,354 L300,320 L300,332 L0,368 Z" fill="#7d3c98" opacity=".85"/>
          <path d="M0,372 L300,336 L300,352 L0,390 Z" fill="#9b59b6" opacity=".9"/>
        </g>
        <!-- Lavender stalks detail (front rows) -->
        <g stroke="#c39bd3" stroke-width="1.2" opacity=".6">
          <line x1="10" y1="375" x2="12" y2="350"/><line x1="25" y1="378" x2="27" y2="352"/>
          <line x1="42" y1="376" x2="44" y2="349"/><line x1="58" y1="374" x2="60" y2="350"/>
          <line x1="75" y1="377" x2="77" y2="351"/><line x1="92" y1="375" x2="94" y2="348"/>
          <line x1="110" y1="376" x2="112" y2="350"/><line x1="128" y1="374" x2="130" y2="348"/>
          <line x1="147" y1="376" x2="149" y2="349"/><line x1="165" y1="375" x2="167" y2="350"/>
          <line x1="183" y1="377" x2="185" y2="351"/><line x1="200" y1="374" x2="202" y2="349"/>
          <line x1="218" y1="376" x2="220" y2="350"/><line x1="236" y1="375" x2="238" y2="348"/>
          <line x1="254" y1="377" x2="256" y2="352"/><line x1="272" y1="374" x2="274" y2="349"/>
          <line x1="288" y1="376" x2="290" y2="350"/>
        </g>
        <!-- Farmhouse silhouette -->
        <g fill="#2a1240">
          <rect x="120" y="255" width="45" height="25"/>
          <polygon points="115,255 150,238 185,255"/>
          <rect x="137" y="265" width="10" height="15"/>
        </g>
        <!-- Lone cypress tree -->
        <g fill="#1a0a30">
          <rect x="218" y="240" width="5" height="35"/>
          <ellipse cx="220" cy="235" rx="8" ry="18"/>
        </g>
      </svg>
      <figcaption class="ig-01__caption"><span>Provence at Dusk</span><small>Landscape · France</small></figcaption>
    </figure>

    <!-- SHORT 2: Coral reef underwater -->
    <figure class="ig-01__card ig-01__card--short">
      <svg class="ig-01__scene" viewBox="0 0 300 225" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <linearGradient id="ocean" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stop-color="#0066aa"/>
            <stop offset="40%" stop-color="#004488"/>
            <stop offset="100%" stop-color="#002255"/>
          </linearGradient>
          <filter id="wf"><feGaussianBlur stdDeviation="2"/></filter>
        </defs>
        <rect width="300" height="225" fill="url(#ocean)"/>
        <!-- Light caustics from above -->
        <g opacity=".25" stroke="#a0e0ff" fill="none">
          <ellipse cx="60" cy="30" rx="18" ry="8" transform="rotate(-20,60,30)"/>
          <ellipse cx="120" cy="20" rx="22" ry="10" transform="rotate(15,120,20)"/>
          <ellipse cx="200" cy="35" rx="16" ry="7" transform="rotate(-10,200,35)"/>
          <ellipse cx="260" cy="25" rx="20" ry="9" transform="rotate(25,260,25)"/>
          <ellipse cx="90" cy="55" rx="14" ry="6" transform="rotate(5,90,55)"/>
          <ellipse cx="180" cy="45" rx="18" ry="7" transform="rotate(-15,180,45)"/>
        </g>
        <!-- Light shafts -->
        <g opacity=".08" fill="#a0e0ff">
          <polygon points="80,0 110,0 160,225 130,225"/>
          <polygon points="170,0 195,0 240,225 215,225"/>
        </g>
        <!-- Sand bottom -->
        <path d="M0,180 Q75,168 150,175 Q225,168 300,178 L300,225 L0,225 Z" fill="#c8a060"/>
        <!-- Coral formations -->
        <!-- Brain coral -->
        <ellipse cx="50" cy="180" rx="28" ry="18" fill="#e06030"/>
        <g stroke="#c05020" stroke-width="1" fill="none" opacity=".6">
          <path d="M28,180 Q40,172 50,180 Q60,188 72,180"/>
          <path d="M30,175 Q42,168 52,175 Q62,182 70,175"/>
          <path d="M32,185 Q44,178 54,185 Q62,192 68,185"/>
        </g>
        <!-- Fan coral -->
        <g stroke="#ff6090" stroke-width="1.5" fill="none">
          <path d="M130,225 Q125,180 120,150 Q115,120 130,100"/>
          <path d="M130,200 Q145,170 155,145 Q162,125 155,105"/>
          <path d="M130,195 Q112,168 108,145 Q106,125 115,108"/>
          <path d="M108,145 L155,145" stroke-width=".8" opacity=".5"/>
          <path d="M112,165 L158,162" stroke-width=".8" opacity=".5"/>
          <path d="M118,185 L152,182" stroke-width=".8" opacity=".5"/>
        </g>
        <!-- Staghorn coral -->
        <g stroke="#f0a050" stroke-width="2.5" fill="none" stroke-linecap="round">
          <line x1="200" y1="225" x2="205" y2="195"/>
          <line x1="205" y1="195" x2="195" y2="175"/><line x1="205" y1="195" x2="215" y2="178"/>
          <line x1="195" y1="175" x2="188" y2="158"/><line x1="195" y1="175" x2="200" y2="160"/>
          <line x1="215" y1="178" x2="210" y2="162"/><line x1="215" y1="178" x2="225" y2="165"/>
          <line x1="220" y1="225" x2="225" y2="192"/>
          <line x1="225" y1="192" x2="218" y2="172"/><line x1="225" y1="192" x2="235" y2="175"/>
        </g>
        <!-- Sea anemone -->
        <g transform="translate(255,185)">
          <ellipse cx="0" cy="8" rx="14" ry="8" fill="#4a1a6a"/>
          <g stroke="#c060e0" stroke-width="1.5" fill="none" stroke-linecap="round">
            <path d="M-8,5 Q-12,-5 -10,-18"/><path d="M-4,3 Q-5,-8 -2,-20"/>
            <path d="M0,2 Q2,-10 3,-22"/><path d="M4,3 Q8,-7 9,-19"/>
            <path d="M8,5 Q14,-3 14,-16"/>
          </g>
        </g>
        <!-- Tropical fish -->
        <g transform="translate(80,100)">
          <ellipse cx="0" cy="0" rx="12" ry="7" fill="#ff6b00"/>
          <polygon points="12,0 20,-6 20,6" fill="#ff6b00"/>
          <line x1="-8" y1="-7" x2="-8" y2="7" stroke="#fff" stroke-width="2"/>
          <line x1="-2" y1="-7" x2="-2" y2="7" stroke="#fff" stroke-width="2"/>
          <circle cx="-8" cy="-1" r="2" fill="#333"/>
        </g>
        <g transform="translate(220,80) scale(.8)">
          <ellipse cx="0" cy="0" rx="12" ry="7" fill="#ffdd00"/>
          <polygon points="12,0 20,-6 20,6" fill="#ffdd00"/>
          <line x1="-4" y1="-7" x2="-4" y2="7" stroke="#1a1a00" stroke-width="2"/>
          <circle cx="-8" cy="-1" r="2" fill="#333"/>
        </g>
        <!-- Sea turtle -->
        <g transform="translate(160,130)">
          <ellipse cx="0" cy="0" rx="16" ry="11" fill="#2d6a4f"/>
          <ellipse cx="0" cy="0" rx="12" ry="8" fill="#40916c" opacity=".8"/>
          <ellipse cx="-14" cy="-3" rx="6" ry="3" fill="#2d6a4f" transform="rotate(-30,-14,-3)"/>
          <ellipse cx="14" cy="-3" rx="6" ry="3" fill="#2d6a4f" transform="rotate(30,14,-3)"/>
          <ellipse cx="-12" cy="5" rx="5" ry="2.5" fill="#2d6a4f" transform="rotate(20,-12,5)"/>
          <ellipse cx="12" cy="5" rx="5" ry="2.5" fill="#2d6a4f" transform="rotate(-20,12,5)"/>
          <ellipse cx="-18" cy="0" rx="5" ry="3" fill="#1b4332"/>
          <circle cx="-21" cy="-1" r="1.5" fill="#333"/>
        </g>
      </svg>
      <figcaption class="ig-01__caption"><span>Coral Triangle</span><small>Marine · Raja Ampat</small></figcaption>
    </figure>

    <!-- MID 2: Milky Way desert -->
    <figure class="ig-01__card ig-01__card--mid">
      <svg class="ig-01__scene" viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <radialGradient id="mwbg" cx="50%" cy="30%" r="70%">
            <stop offset="0%" stop-color="#1a0a35"/>
            <stop offset="50%" stop-color="#0d0520"/>
            <stop offset="100%" stop-color="#05020e"/>
          </radialGradient>
          <filter id="mwf"><feGaussianBlur stdDeviation="3"/></filter>
          <filter id="mwf2"><feGaussianBlur stdDeviation="1"/></filter>
        </defs>
        <rect width="300" height="300" fill="url(#mwbg)"/>
        <!-- Milky Way band -->
        <ellipse cx="160" cy="130" rx="180" ry="60" fill="#a080c0" opacity=".12" transform="rotate(-30,160,130)" filter="url(#mwf)"/>
        <ellipse cx="150" cy="125" rx="150" ry="40" fill="#8060b0" opacity=".18" transform="rotate(-30,150,125)" filter="url(#mwf)"/>
        <ellipse cx="145" cy="120" rx="120" ry="28" fill="#c0a0e0" opacity=".15" transform="rotate(-30,145,120)" filter="url(#mwf2)"/>
        <!-- Stars — many tiny -->
        <g fill="#fff">
          <circle cx="20" cy="15" r="1.2" opacity=".9"/><circle cx="45" cy="8" r=".9" opacity=".8"/>
          <circle cx="70" cy="20" r="1.1" opacity=".85"/><circle cx="95" cy="10" r=".8" opacity=".7"/>
          <circle cx="120" cy="18" r="1" opacity=".9"/><circle cx="145" cy="5" r="1.3" opacity=".95"/>
          <circle cx="170" cy="22" r=".9" opacity=".8"/><circle cx="195" cy="12" r="1.1" opacity=".85"/>
          <circle cx="220" cy="20" r=".8" opacity=".75"/><circle cx="245" cy="8" r="1.2" opacity=".9"/>
          <circle cx="270" cy="18" r="1" opacity=".85"/><circle cx="285" cy="5" r=".9" opacity=".8"/>
          <circle cx="10" cy="40" r=".8" opacity=".7"/><circle cx="35" cy="50" r="1" opacity=".85"/>
          <circle cx="60" cy="38" r="1.2" opacity=".9"/><circle cx="88" cy="55" r=".9" opacity=".75"/>
          <circle cx="115" cy="42" r="1.1" opacity=".88"/><circle cx="140" cy="58" r=".8" opacity=".7"/>
          <circle cx="165" cy="48" r="1.3" opacity=".95"/><circle cx="192" cy="35" r=".9" opacity=".8"/>
          <circle cx="218" cy="52" r="1" opacity=".85"/><circle cx="243" cy="42" r="1.2" opacity=".9"/>
          <circle cx="265" cy="55" r=".8" opacity=".75"/><circle cx="290" cy="40" r="1.1" opacity=".88"/>
          <circle cx="25" cy="75" r="1" opacity=".8"/><circle cx="55" cy="80" r=".9" opacity=".75"/>
          <circle cx="80" cy="70" r="1.2" opacity=".9"/><circle cx="105" cy="85" r=".8" opacity=".7"/>
          <circle cx="130" cy="72" r="1.1" opacity=".85"/><circle cx="158" cy="88" r="1" opacity=".82"/>
          <circle cx="185" cy="75" r=".9" opacity=".78"/><circle cx="210" cy="82" r="1.2" opacity=".9"/>
          <circle cx="235" cy="70" r=".8" opacity=".72"/><circle cx="260" cy="85" r="1.1" opacity=".88"/>
          <circle cx="282" cy="72" r="1" opacity=".8"/>
          <!-- Bright stars -->
          <circle cx="50" cy="30" r="2" opacity=".95"/><circle cx="200" cy="55" r="1.8" opacity=".95"/>
          <circle cx="100" cy="25" r="1.7" opacity=".9"/><circle cx="260" cy="35" r="2.1" opacity=".98"/>
          <circle cx="155" cy="60" r="1.9" opacity=".92"/>
        </g>
        <!-- Shooting star -->
        <line x1="220" y1="40" x2="260" y2="20" stroke="rgba(255,255,220,.8)" stroke-width="1.5" stroke-linecap="round"/>
        <circle cx="220" cy="40" r="2" fill="#ffffc0" opacity=".9"/>
        <!-- Desert sand horizon -->
        <path d="M0,220 Q40,210 70,215 Q100,208 130,218 Q160,210 185,215 Q215,208 245,218 Q270,210 300,215 L300,300 L0,300 Z" fill="#c8a060"/>
        <!-- Sand dunes -->
        <path d="M0,235 Q60,218 120,228 Q180,218 240,232 Q270,225 300,228 L300,300 L0,300 Z" fill="#b89050"/>
        <path d="M0,255 Q80,238 160,248 Q230,238 300,250 L300,300 L0,300 Z" fill="#a07840"/>
        <!-- Desert rocks silhouettes -->
        <g fill="#5a3a18">
          <path d="M0,240 Q15,220 30,228 Q45,215 60,232"/>
          <path d="M245,235 Q258,218 272,226 Q285,215 300,230"/>
        </g>
        <!-- Lone cactus -->
        <g fill="#1a3a1a">
          <rect x="148" y="195" width="8" height="30"/>
          <rect x="135" y="203" width="20" height="5" rx="2"/>
          <rect x="135" y="203" width="5" height="12" rx="2"/>
          <rect x="163" y="203" width="5" height="10" rx="2"/>
        </g>
        <!-- Person silhouette stargazing -->
        <g fill="#0d0620">
          <ellipse cx="155" cy="237" rx="6" ry="8"/> <!-- body sitting -->
          <circle cx="155" cy="227" r="5"/> <!-- head -->
        </g>
      </svg>
      <figcaption class="ig-01__caption"><span>Milky Way, Sahara</span><small>Astrophoto · Morocco</small></figcaption>
    </figure>

    <!-- SHORT 3: Misty bamboo forest -->
    <figure class="ig-01__card ig-01__card--short">
      <svg class="ig-01__scene" viewBox="0 0 300 225" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <linearGradient id="bamboo-sky" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stop-color="#d0e8c0"/>
            <stop offset="50%" stop-color="#e8f4d8"/>
            <stop offset="100%" stop-color="#c8dca8"/>
          </linearGradient>
          <filter id="mist"><feGaussianBlur stdDeviation="6"/></filter>
          <filter id="mist2"><feGaussianBlur stdDeviation="2"/></filter>
        </defs>
        <rect width="300" height="225" fill="url(#bamboo-sky)"/>
        <!-- Mist layers -->
        <rect x="0" y="140" width="300" height="50" fill="#e8f4d0" opacity=".6" filter="url(#mist)"/>
        <rect x="0" y="160" width="300" height="40" fill="#f0f8e0" opacity=".5" filter="url(#mist)"/>
        <rect x="0" y="180" width="300" height="45" fill="#f5faea" opacity=".55" filter="url(#mist)"/>
        <!-- Back bamboo (thin, lighter) -->
        <g stroke="#6a9a40" stroke-width="4" fill="none" opacity=".4">
          <line x1="15" y1="0" x2="15" y2="225"/>
          <line x1="35" y1="0" x2="33" y2="225"/>
          <line x1="55" y1="0" x2="57" y2="225"/>
          <line x1="85" y1="0" x2="83" y2="225"/>
          <line x1="105" y1="0" x2="107" y2="225"/>
          <line x1="140" y1="0" x2="138" y2="225"/>
          <line x1="175" y1="0" x2="177" y2="225"/>
          <line x1="210" y1="0" x2="208" y2="225"/>
          <line x1="240" y1="0" x2="242" y2="225"/>
          <line x1="265" y1="0" x2="263" y2="225"/>
          <line x1="285" y1="0" x2="287" y2="225"/>
        </g>
        <!-- Mid bamboo nodes -->
        <g fill="none">
          <g stroke="#5a8a30" stroke-width="6" opacity=".6">
            <line x1="25" y1="0" x2="23" y2="225"/>
            <line x1="70" y1="0" x2="72" y2="225"/>
            <line x1="120" y1="0" x2="118" y2="225"/>
            <line x1="160" y1="0" x2="162" y2="225"/>
            <line x1="200" y1="0" x2="198" y2="225"/>
            <line x1="250" y1="0" x2="252" y2="225"/>
            <line x1="295" y1="0" x2="293" y2="225"/>
          </g>
          <!-- Node rings -->
          <g stroke="#4a7a28" stroke-width="2" opacity=".5">
            <line x1="21" y1="40" x2="27" y2="40"/><line x1="21" y1="80" x2="27" y2="80"/>
            <line x1="21" y1="120" x2="27" y2="120"/><line x1="21" y1="160" x2="27" y2="160"/>
            <line x1="68" y1="55" x2="74" y2="55"/><line x1="68" y1="100" x2="74" y2="100"/>
            <line x1="68" y1="145" x2="74" y2="145"/><line x1="68" y1="190" x2="74" y2="190"/>
            <line x1="116" y1="35" x2="122" y2="35"/><line x1="116" y1="75" x2="122" y2="75"/>
            <line x1="116" y1="115" x2="122" y2="115"/><line x1="116" y1="155" x2="122" y2="155"/>
            <line x1="116" y1="200" x2="122" y2="200"/>
            <line x1="158" y1="45" x2="164" y2="45"/><line x1="158" y1="90" x2="164" y2="90"/>
            <line x1="158" y1="135" x2="164" y2="135"/><line x1="158" y1="180" x2="164" y2="180"/>
          </g>
        </g>
        <!-- Front bamboo (thick, dark) -->
        <g stroke="#3a6a1a" stroke-width="9" fill="none" opacity=".85">
          <line x1="48" y1="0" x2="46" y2="225"/>
          <line x1="148" y1="0" x2="150" y2="225"/>
          <line x1="225" y1="0" x2="223" y2="225"/>
        </g>
        <!-- Front bamboo nodes -->
        <g fill="none" stroke="#2d5412" stroke-width="3" opacity=".8">
          <line x1="44" y1="50" x2="50" y2="50"/><line x1="44" y1="110" x2="50" y2="110"/>
          <line x1="44" y1="170" x2="50" y2="170"/>
          <line x1="146" y1="60" x2="152" y2="60"/><line x1="146" y1="125" x2="152" y2="125"/>
          <line x1="146" y1="190" x2="152" y2="190"/>
          <line x1="221" y1="45" x2="227" y2="45"/><line x1="221" y1="105" x2="227" y2="105"/>
          <line x1="221" y1="165" x2="227" y2="165"/>
        </g>
        <!-- Bamboo leaves -->
        <g fill="#5a9a28" opacity=".7">
          <ellipse cx="38" cy="55" rx="18" ry="5" transform="rotate(-35,38,55)"/>
          <ellipse cx="55" cy="48" rx="20" ry="5" transform="rotate(25,55,48)"/>
          <ellipse cx="140" cy="68" rx="18" ry="5" transform="rotate(-40,140,68)"/>
          <ellipse cx="155" cy="58" rx="22" ry="5" transform="rotate(20,155,58)"/>
          <ellipse cx="215" cy="52" rx="18" ry="5" transform="rotate(-30,215,52)"/>
          <ellipse cx="230" cy="45" rx="20" ry="5" transform="rotate(35,230,45)"/>
          <ellipse cx="70" cy="108" rx="16" ry="4" transform="rotate(-25,70,108)"/>
          <ellipse cx="118" cy="78" rx="19" ry="5" transform="rotate(28,118,78)"/>
          <ellipse cx="200" cy="100" rx="17" ry="4" transform="rotate(-32,200,100)"/>
        </g>
        <!-- Ground -->
        <rect x="0" y="208" width="300" height="17" fill="#8aaa50" opacity=".5"/>
        <!-- Path stones -->
        <g fill="#a09070" opacity=".6">
          <ellipse cx="80" cy="216" rx="14" ry="6" transform="rotate(8,80,216)"/>
          <ellipse cx="140" cy="219" rx="16" ry="5" transform="rotate(-5,140,219)"/>
          <ellipse cx="210" cy="215" rx="12" ry="5" transform="rotate(12,210,215)"/>
        </g>
      </svg>
      <figcaption class="ig-01__caption"><span>Morning Mist</span><small>Forest · Arashiyama</small></figcaption>
    </figure>

  </div>
</div>
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
body{background:#f0ebe3;font-family:'DM Sans',sans-serif;padding:2rem}

.ig-01{--gap:.75rem;--radius:14px;font-family:'DM Sans',sans-serif}
.ig-01__grid{columns:3;column-gap:var(--gap)}
.ig-01__card{break-inside:avoid;margin-bottom:var(--gap);border-radius:var(--radius);overflow:hidden;position:relative;cursor:pointer;display:block}
.ig-01__card--tall .ig-01__scene{aspect-ratio:3/4}
.ig-01__card--mid  .ig-01__scene{aspect-ratio:1/1}
.ig-01__card--short .ig-01__scene{aspect-ratio:4/3}
.ig-01__scene{width:100%;display:block;transition:transform .5s cubic-bezier(.25,.46,.45,.94)}
.ig-01__card:hover .ig-01__scene{transform:scale(1.06)}
.ig-01__caption{position:absolute;inset:0;display:flex;flex-direction:column;justify-content:flex-end;padding:1.25rem 1rem;background:linear-gradient(to top,rgba(15,10,5,.85),transparent 60%);opacity:0;transform:translateY(8px);transition:opacity .35s ease,transform .35s ease}
.ig-01__card:hover .ig-01__caption{opacity:1;transform:translateY(0)}
.ig-01__caption span{color:#fff;font-size:.9rem;font-weight:600;font-family:'Playfair Display',serif}
.ig-01__caption small{color:rgba(255,255,255,.6);font-size:.72rem;margin-top:.2rem;display:block;letter-spacing:.06em}
@media(prefers-reduced-motion:reduce){.ig-01__scene,.ig-01__caption{transition:none}}

How this works

The masonry layout is achieved with CSS columns: 3 on the grid wrapper and break-inside: avoid on each card, letting the browser flow cards into the shortest column automatically. Each card uses overflow: hidden so the scene SVG clips cleanly within the rounded corners.

Hover interactivity uses a nested selector: .ig-01__card:hover .ig-01__scene applies a transform: scale(1.06) with a smooth cubic-bezier transition, while .ig-01__card:hover .ig-01__caption fades in via opacity and a translateY lift — both driven purely by CSS without any JavaScript.

Customize

  • Change the column count by editing columns: 3 on .ig-01__grid — try 4 for denser layouts or 2 for a wider editorial feel.
  • Adjust the zoom intensity on hover by changing scale(1.06) in .ig-01__card:hover .ig-01__scene — values between 1.03 and 1.12 work well.
  • Swap aspect ratios by changing the aspect-ratio on .ig-01__card--tall / --mid / --short to create more dramatic height variation.
  • Extend the caption background by changing the gradient stop from 60% to 80% in .ig-01__caption for more text legibility.
  • Add a gap rhythm by editing --gap: .75rem on .ig-01 — increasing to 1.25rem gives a more airy editorial look.

Watch out for

  • CSS columns distributes cards top-to-bottom in each column, not left-to-right — so the visual reading order may not match DOM order. Use order properties or reorder the HTML if sequence matters for accessibility.
  • Safari clips the hover transform if overflow: hidden is set on a parent with border-radius; wrap the card content in an inner div and apply overflow: hidden there instead.
  • The columns layout rebalances on viewport resize, which can cause cards to jump columns mid-layout. Set a column-fill: balance explicitly and consider min-height on columns to reduce reflow.

Browser support

ChromeSafariFirefoxEdge
66+ 13+ 52+ 66+

CSS columns and break-inside are broadly supported; aspect-ratio requires Chrome 88+, Safari 15+.

Search CodeFronts

Loading…