16 CSS Image Gallery Designs 07 / 16

CSS Mosaic Zoom Gallery

Editorial "Earth's Biomes" mosaic dashboard — 8 illustrated biome tiles in a 4×3 grid where clicking any tile expands it to a 2×2 featured hero.

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

The code

<div class="ig-07">

  <!-- Gallery chrome -->
  <header class="ig-07__chrome">
    <div class="ig-07__chrome-l">
      <span class="ig-07__title">Earth's Biomes</span>
      <span class="ig-07__sub">8 scenes · Click any tile to feature</span>
    </div>
    <div class="ig-07__chrome-r">
      <span class="ig-07__pill" id="ig-07-counter">Featured · 01 / 08</span>
    </div>
  </header>

  <div class="ig-07__grid" id="ig-07-grid">

    <!-- 1: Amazon rainforest canopy -->
    <div class="ig-07__tile ig-07__tile--active" data-idx="0" data-cap="Amazon Canopy" data-loc="Brazil · Rainforest" data-color="#2a9018">
      <svg viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
        <defs><radialGradient id="rfg" cx="50%" cy="30%" r="70%"><stop offset="0%" stop-color="#1a5a0a"/><stop offset="60%" stop-color="#0d3a05"/><stop offset="100%" stop-color="#051a02"/></radialGradient><filter id="rff"><feGaussianBlur stdDeviation="5"/></filter></defs>
        <rect width="400" height="400" fill="url(#rfg)"/>
        <g opacity=".2" fill="#60ff20"><polygon points="140,0 160,0 240,400 220,400"/><polygon points="200,0 215,0 280,400 265,400"/></g>
        <!-- Dense canopy layers -->
        <g fill="#0a4008"><ellipse cx="60" cy="60" rx="75" ry="55"/><ellipse cx="170" cy="40" rx="90" ry="60"/><ellipse cx="290" cy="55" rx="80" ry="58"/><ellipse cx="370" cy="45" rx="65" ry="50"/><ellipse cx="30" cy="130" rx="60" ry="45"/><ellipse cx="130" cy="120" rx="80" ry="55"/><ellipse cx="250" cy="110" rx="85" ry="58"/><ellipse cx="370" cy="125" rx="65" ry="48"/></g>
        <g fill="#1a7010"><ellipse cx="60" cy="48" rx="65" ry="48"/><ellipse cx="170" cy="28" rx="80" ry="52"/><ellipse cx="290" cy="42" rx="70" ry="50"/><ellipse cx="365" cy="32" rx="58" ry="44"/><ellipse cx="115" cy="108" rx="70" ry="48"/><ellipse cx="245" cy="98" rx="75" ry="50"/></g>
        <g fill="#2a9018"><ellipse cx="60" cy="38" rx="55" ry="40"/><ellipse cx="170" cy="18" rx="68" ry="44"/><ellipse cx="290" cy="30" rx="62" ry="42"/><ellipse cx="365" cy="22" rx="50" ry="36"/></g>
        <!-- Bromeliads and epiphytes -->
        <g fill="#50c030" opacity=".7"><ellipse cx="195" cy="30" rx="18" ry="8" transform="rotate(-25,195,30)"/><ellipse cx="310" cy="22" rx="16" ry="7" transform="rotate(20,310,22)"/><ellipse cx="85" cy="40" rx="14" ry="6" transform="rotate(-15,85,40)"/></g>
        <!-- Macaw pair -->
        <g transform="translate(200,220)">
          <ellipse cx="0" cy="0" rx="14" ry="20" fill="#2266cc"/>
          <ellipse cx="0" cy="-5" rx="9" ry="12" fill="#1a55bb"/>
          <path d="M-8,5 Q-20,5 -25,12" stroke="#2266cc" stroke-width="5" fill="none" stroke-linecap="round"/>
          <path d="M8,5 Q20,8 25,14" stroke="#2266cc" stroke-width="5" fill="none" stroke-linecap="round"/>
          <ellipse cx="0" cy="10" rx="8" ry="6" fill="#ff6600"/>
          <circle cx="-5" cy="-12" r="4" fill="#1a1a14"/>
          <circle cx="-4" cy="-13" r="1.5" fill="#fff"/>
          <path d="M-10,-10 Q-14,-6 -12,-4" fill="#ffcc00" stroke="none"/>
        </g>
        <!-- Hanging vine -->
        <path d="M320,0 Q310,100 330,200 Q315,300 325,400" stroke="#1a5a0a" stroke-width="4" fill="none"/>
        <g fill="#2a8020" opacity=".6"><ellipse cx="316" cy="85" rx="12" ry="6" transform="rotate(-30,316,85)"/><ellipse cx="324" cy="170" rx="11" ry="5" transform="rotate(25,324,170)"/><ellipse cx="320" cy="270" rx="12" ry="6" transform="rotate(-20,320,270)"/></g>
        <!-- Mist at ground level -->
        <rect x="0" y="340" width="400" height="60" fill="#88cc68" opacity=".15" filter="url(#rff)"/>
        <!-- Blue morpho butterfly -->
        <g transform="translate(120,310)">
          <ellipse cx="-12" cy="0" rx="18" ry="12" fill="#1a88ff" transform="rotate(25,-12,0)"/>
          <ellipse cx="12" cy="0" rx="18" ry="12" fill="#1a88ff" transform="rotate(-25,12,0)"/>
          <ellipse cx="-8" cy="2" rx="10" ry="7" fill="#44aaff" opacity=".6" transform="rotate(25,-8,2)"/>
          <ellipse cx="8" cy="2" rx="10" ry="7" fill="#44aaff" opacity=".6" transform="rotate(-25,8,2)"/>
          <rect x="-1.5" y="-10" width="3" height="20" rx="1.5" fill="#0a1a0a"/>
          <path d="M0,-10 Q5,-18 8,-22 M0,-10 Q-5,-18 -8,-22" stroke="#0a1a0a" stroke-width="1" fill="none"/>
        </g>
      </svg>
      <span class="ig-07__cap"><strong>Amazon Canopy</strong><em>Brazil · Rainforest</em></span>
      <span class="ig-07__num">01</span>
    </div>

    <!-- 2: Arctic tundra -->
    <div class="ig-07__tile" data-idx="1" data-cap="Arctic Tundra" data-loc="Svalbard · 78° N" data-color="#88ccdd">
      <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
        <defs><linearGradient id="atg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#b8d8f0"/><stop offset="50%" stop-color="#d0e8f8"/><stop offset="100%" stop-color="#c8d8e8"/></linearGradient><filter id="atf"><feGaussianBlur stdDeviation="4"/></filter></defs>
        <rect width="200" height="200" fill="url(#atg)"/>
        <!-- Aurora hint -->
        <path d="M0,60 Q100,30 200,55" stroke="#40e8a0" stroke-width="18" fill="none" opacity=".3" filter="url(#atf)"/>
        <!-- Snow plain -->
        <path d="M0,130 Q50,118 100,125 Q150,118 200,128 L200,200 L0,200 Z" fill="#e0eef8"/>
        <path d="M0,148 Q50,138 100,144 Q150,138 200,146 L200,200 L0,200 Z" fill="#d0e4f4"/>
        <!-- Ice formations -->
        <g fill="#b8d0e8"><polygon points="25,130 35,108 45,130"/><polygon points="150,128 158,110 166,128"/></g>
        <!-- Musk ox herd silhouette -->
        <g fill="#2a1a08" transform="translate(30,148)">
          <ellipse cx="0" cy="0" rx="16" ry="10"/><ellipse cx="-8" cy="-6" rx="8" ry="9"/><rect x="-14" y="8" width="5" height="12" rx="2"/><rect x="-4" y="8" width="5" height="12" rx="2"/><rect x="5" y="8" width="5" height="12" rx="2"/><rect x="12" y="8" width="5" height="10" rx="2"/>
        </g>
        <g fill="#2a1a08" transform="translate(90,152)">
          <ellipse cx="0" cy="0" rx="13" ry="8"/><ellipse cx="-6" cy="-5" rx="6" ry="7"/><rect x="-11" y="7" width="4" height="10" rx="2"/><rect x="-3" y="7" width="4" height="10" rx="2"/><rect x="5" y="7" width="4" height="10" rx="2"/><rect x="11" y="7" width="4" height="8" rx="2"/>
        </g>
        <!-- Stars -->
        <g fill="#fff" opacity=".8"><circle cx="20" cy="15" r="1"/><circle cx="55" cy="8" r=".9"/><circle cx="100" cy="18" r="1.1"/><circle cx="145" cy="5" r=".9"/><circle cx="185" cy="14" r="1"/></g>
      </svg>
      <span class="ig-07__cap"><strong>Arctic Tundra</strong><em>Svalbard · 78° N</em></span>
      <span class="ig-07__num">02</span>
    </div>

    <!-- 3: Sahara dune -->
    <div class="ig-07__tile" data-idx="2" data-cap="Sahara Dunes" data-loc="Morocco · Erg Chebbi" data-color="#e8a838">
      <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
        <defs><linearGradient id="sdg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#e8b840"/><stop offset="50%" stop-color="#d09828"/><stop offset="100%" stop-color="#8a5010"/></linearGradient></defs>
        <rect width="200" height="200" fill="url(#sdg)"/>
        <circle cx="100" cy="55" r="28" fill="#fff8c0" opacity=".9"/>
        <!-- Dune silhouettes -->
        <path d="M0,120 Q50,85 100,100 Q150,82 200,105 L200,200 L0,200 Z" fill="#c08828"/>
        <path d="M0,145 Q60,125 120,138 Q170,126 200,140 L200,200 L0,200 Z" fill="#a87020"/>
        <path d="M0,165 Q80,150 160,162 Q185,156 200,162 L200,200 L0,200 Z" fill="#8a5810"/>
        <!-- Ripple texture -->
        <g stroke="#c09030" stroke-width=".8" fill="none" opacity=".4">
          <path d="M0,132 Q50,125 100,130 Q150,124 200,128"/><path d="M0,142 Q50,136 100,140 Q150,135 200,138"/>
        </g>
        <!-- Scorpion -->
        <g fill="#5a3810" transform="translate(140,160)">
          <ellipse cx="0" cy="0" rx="6" ry="4"/><ellipse cx="-6" cy="0" rx="4" ry="3"/>
          <path d="M6,0 Q12,2 14,8 Q14,14 10,15" stroke="#5a3810" stroke-width="2" fill="none"/>
          <circle cx="10" cy="15" r="2" fill="#5a3810"/>
          <g stroke="#5a3810" stroke-width="1" fill="none"><line x1="-4" y1="-2" x2="-8" y2="-6"/><line x1="-4" y1="2" x2="-8" y2="6"/><line x1="-2" y1="-3" x2="-5" y2="-8"/><line x1="-2" y1="3" x2="-5" y2="8"/></g>
        </g>
      </svg>
      <span class="ig-07__cap"><strong>Sahara Dunes</strong><em>Morocco · Erg Chebbi</em></span>
      <span class="ig-07__num">03</span>
    </div>

    <!-- 4: Deep ocean trench -->
    <div class="ig-07__tile" data-idx="3" data-cap="Deep Ocean" data-loc="Pacific · Hadal zone" data-color="#2a8aee">
      <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
        <defs><radialGradient id="dog" cx="50%" cy="50%" r="60%"><stop offset="0%" stop-color="#0a1a2a"/><stop offset="100%" stop-color="#020508"/></radialGradient><filter id="dof"><feGaussianBlur stdDeviation="3"/></filter></defs>
        <rect width="200" height="200" fill="url(#dog)"/>
        <!-- Bioluminescent creatures -->
        <g filter="url(#dof)">
          <circle cx="60" cy="80" r="8" fill="#00e8ff" opacity=".8"/>
          <circle cx="140" cy="60" r="6" fill="#8040ff" opacity=".8"/>
          <circle cx="100" cy="120" r="10" fill="#00ffaa" opacity=".7"/>
          <circle cx="40" cy="150" r="7" fill="#00e8ff" opacity=".75"/>
          <circle cx="165" cy="140" r="8" fill="#ff40aa" opacity=".8"/>
          <circle cx="90" cy="170" r="6" fill="#40ffcc" opacity=".7"/>
          <circle cx="155" cy="180" r="5" fill="#00e8ff" opacity=".75"/>
        </g>
        <!-- Anglerfish lure -->
        <g transform="translate(110,90)">
          <ellipse cx="0" cy="8" rx="22" ry="14" fill="#0a1520"/>
          <path d="M0,0 Q5,-15 8,-28" stroke="#2a3a4a" stroke-width="2" fill="none"/>
          <circle cx="8" cy="-28" r="6" fill="#00ff88" filter="url(#dof)"/>
          <circle cx="8" cy="-28" r="3" fill="#80ffcc"/>
          <!-- Teeth -->
          <g fill="#a0b8c0"><polygon points="-15,2 -12,-4 -9,2"/><polygon points="-6,2 -3,-5 0,2"/><polygon points="3,2 6,-4 9,2"/><polygon points="12,2 15,-3 18,2"/></g>
          <!-- Eye -->
          <circle cx="-12" cy="2" r="4" fill="#0a0a14"/><circle cx="-11" cy="1" r="1.5" fill="#00ffaa" opacity=".8"/>
        </g>
        <!-- Tube worms -->
        <g stroke="#ff6080" stroke-width="2" fill="none" opacity=".6" stroke-linecap="round">
          <path d="M20,200 Q18,170 22,155 Q26,145 22,130"/><path d="M30,200 Q28,168 32,152 Q36,142 30,128"/>
          <path d="M168,200 Q172,172 168,158 Q165,148 170,134"/>
          <!-- Tube tops -->
          <circle cx="22" cy="130" r="4" fill="#ff4060" stroke="none"/><circle cx="30" cy="128" r="4" fill="#ff4060" stroke="none"/>
          <circle cx="170" cy="134" r="4" fill="#ff4060" stroke="none"/>
        </g>
        <!-- Particles drifting -->
        <g fill="#80c8ff" opacity=".4"><circle cx="50" cy="40" r="1.2"/><circle cx="120" cy="35" r="1"/><circle cx="175" cy="55" r="1.3"/><circle cx="30" cy="100" r="1.1"/><circle cx="180" cy="110" r="1.2"/></g>
      </svg>
      <span class="ig-07__cap"><strong>Deep Ocean</strong><em>Pacific · Hadal zone</em></span>
      <span class="ig-07__num">04</span>
    </div>

    <!-- 5: Aerial city -->
    <div class="ig-07__tile" data-idx="4" data-cap="City Aerial" data-loc="Manhattan · Night" data-color="#7c6cff">
      <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
        <rect width="200" height="200" fill="#0a0c18"/>
        <!-- City grid from above -->
        <g fill="none" stroke="#1a2a4a" stroke-width="1.5"><line x1="0" y1="40" x2="200" y2="40"/><line x1="0" y1="80" x2="200" y2="80"/><line x1="0" y1="120" x2="200" y2="120"/><line x1="0" y1="160" x2="200" y2="160"/><line x1="40" y1="0" x2="40" y2="200"/><line x1="80" y1="0" x2="80" y2="200"/><line x1="120" y1="0" x2="120" y2="200"/><line x1="160" y1="0" x2="160" y2="200"/></g>
        <!-- Building blocks (top-down) -->
        <g fill="#1a2a3a"><rect x="5" y="5" width="30" height="30" rx="2"/><rect x="45" y="5" width="28" height="30" rx="2"/><rect x="85" y="5" width="30" height="30" rx="2"/><rect x="45" y="45" width="30" height="30" rx="2"/><rect x="85" y="45" width="28" height="30" rx="2"/><rect x="125" y="5" width="30" height="30" rx="2"/><rect x="165" y="5" width="28" height="30" rx="2"/><rect x="5" y="45" width="30" height="30" rx="2"/><rect x="125" y="45" width="30" height="30" rx="2"/><rect x="165" y="45" width="28" height="30" rx="2"/><rect x="5" y="85" width="30" height="30" rx="2"/><rect x="45" y="85" width="30" height="30" rx="2"/><rect x="85" y="85" width="28" height="30" rx="2"/><rect x="125" y="85" width="30" height="30" rx="2"/><rect x="165" y="85" width="28" height="30" rx="2"/><rect x="5" y="125" width="30" height="30" rx="2"/><rect x="45" y="125" width="28" height="30" rx="2"/><rect x="85" y="125" width="30" height="30" rx="2"/><rect x="125" y="125" width="28" height="30" rx="2"/><rect x="165" y="125" width="30" height="30" rx="2"/><rect x="5" y="165" width="30" height="28" rx="2"/><rect x="45" y="165" width="30" height="28" rx="2"/><rect x="85" y="165" width="28" height="28" rx="2"/><rect x="125" y="165" width="30" height="28" rx="2"/><rect x="165" y="165" width="28" height="28" rx="2"/></g>
        <!-- Lit windows -->
        <g fill="#ffd060" opacity=".7"><rect x="8" y="8" width="4" height="4"/><rect x="16" y="8" width="4" height="4"/><rect x="24" y="8" width="4" height="4"/><rect x="48" y="8" width="4" height="4"/><rect x="56" y="8" width="4" height="4"/><rect x="90" y="8" width="4" height="4"/><rect x="98" y="8" width="4" height="4"/><rect x="128" y="8" width="4" height="4"/><rect x="136" y="8" width="4" height="4"/><rect x="48" y="50" width="4" height="4"/><rect x="60" y="50" width="4" height="4"/><rect x="88" y="52" width="4" height="4"/><rect x="96" y="50" width="4" height="4"/><rect x="168" y="8" width="4" height="4"/><rect x="176" y="8" width="4" height="4"/></g>
        <!-- Car lights on roads (streaks) -->
        <g fill="#ff8020" opacity=".6"><rect x="0" y="37" width="12" height="3" rx="1"/><rect x="100" y="77" width="10" height="3" rx="1"/></g>
        <g fill="#ffffff" opacity=".4"><rect x="185" y="42" width="12" height="3" rx="1"/><rect x="70" y="82" width="10" height="3" rx="1"/></g>
        <!-- Park (green block) -->
        <rect x="85" y="125" width="30" height="30" rx="2" fill="#1a4a18"/>
        <g fill="#2a6a28" opacity=".7"><circle cx="95" cy="135" r="5"/><circle cx="108" cy="132" r="4"/><circle cx="100" cy="148" r="5"/></g>
      </svg>
      <span class="ig-07__cap"><strong>City Aerial</strong><em>Manhattan · Night</em></span>
      <span class="ig-07__num">05</span>
    </div>

    <!-- 6: Volcanic island -->
    <div class="ig-07__tile" data-idx="5" data-cap="Volcanic Island" data-loc="Iceland · Fagradalsfjall" data-color="#ff5a32">
      <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
        <defs><radialGradient id="vig" cx="50%" cy="40%" r="60%"><stop offset="0%" stop-color="#2255aa"/><stop offset="50%" stop-color="#1a4488"/><stop offset="100%" stop-color="#0a1a3a"/></radialGradient><filter id="vif"><feGaussianBlur stdDeviation="3"/></filter></defs>
        <rect width="200" height="200" fill="url(#vig)"/>
        <!-- Ocean waves -->
        <g stroke="#3388cc" stroke-width="1.5" fill="none" opacity=".5"><path d="M0,155 Q50,145 100,152 Q150,145 200,150"/><path d="M0,165 Q50,156 100,162 Q150,156 200,160"/><path d="M0,175 Q50,167 100,172 Q150,167 200,172"/></g>
        <!-- Island volcano -->
        <polygon points="50,200 80,130 100,95 120,130 150,200" fill="#2a2010"/>
        <polygon points="60,200 80,130 100,95 120,130 140,200" fill="#3a3018"/>
        <!-- Lava flow -->
        <path d="M100,97 Q95,120 88,145 Q82,165 85,200" stroke="#ff4400" stroke-width="4" fill="none" opacity=".8"/>
        <path d="M100,97 Q104,118 110,140 Q115,162 112,200" stroke="#ff6600" stroke-width="3" fill="none" opacity=".6"/>
        <!-- Eruption glow -->
        <circle cx="100" cy="95" r="12" fill="#ff4400" opacity=".8" filter="url(#vif)"/>
        <circle cx="100" cy="95" r="5" fill="#ffaa00"/>
        <!-- Smoke plume -->
        <g filter="url(#vif)" opacity=".45"><circle cx="100" cy="78" r="14" fill="#555"/><circle cx="96" cy="62" r="12" fill="#666"/><circle cx="102" cy="46" r="10" fill="#555"/><circle cx="98" cy="32" r="8" fill="#666"/></g>
        <!-- Palm on shore -->
        <line x1="55" y1="200" x2="50" y2="170" stroke="#3a2808" stroke-width="3"/>
        <g fill="#2a6018"><ellipse cx="38" cy="167" rx="14" ry="5" transform="rotate(-30,38,167)"/><ellipse cx="48" cy="162" rx="14" ry="5" transform="rotate(15,48,162)"/><ellipse cx="60" cy="164" rx="12" ry="4" transform="rotate(45,60,164)"/></g>
        <!-- Stars/sky -->
        <g fill="#fff" opacity=".7"><circle cx="20" cy="18" r="1"/><circle cx="60" cy="8" r=".9"/><circle cx="155" cy="12" r="1.1"/><circle cx="185" cy="25" r=".8"/><circle cx="140" cy="35" r="1"/></g>
      </svg>
      <span class="ig-07__cap"><strong>Volcanic Island</strong><em>Iceland · Fagradalsfjall</em></span>
      <span class="ig-07__num">06</span>
    </div>

    <!-- 7: Cherry orchard -->
    <div class="ig-07__tile" data-idx="6" data-cap="Cherry Orchard" data-loc="Kyoto · Spring bloom" data-color="#ff8ab8">
      <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
        <defs><linearGradient id="cog2" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#fce4ec"/><stop offset="50%" stop-color="#f8bbd0"/><stop offset="100%" stop-color="#e8a0b8"/></linearGradient><filter id="cof2"><feGaussianBlur stdDeviation="2"/></filter></defs>
        <rect width="200" height="200" fill="url(#cog2)"/>
        <!-- Tree row in perspective -->
        <g fill="#4a1a08"><rect x="8" y="80" width="8" height="120"/><rect x="55" y="85" width="7" height="115"/><rect x="98" y="88" width="7" height="112"/><rect x="138" y="86" width="7" height="114"/><rect x="178" y="84" width="7" height="116"/></g>
        <!-- Blossom clouds -->
        <g filter="url(#cof2)">
          <circle cx="12" cy="68" r="32" fill="#f48fb1" opacity=".9"/><circle cx="12" cy="55" r="25" fill="#f8c4d4" opacity=".85"/>
          <circle cx="58" cy="72" r="28" fill="#f48fb1" opacity=".88"/><circle cx="58" cy="60" r="22" fill="#f8c4d4" opacity=".8"/>
          <circle cx="101" cy="75" r="26" fill="#f48fb1" opacity=".85"/><circle cx="101" cy="64" r="20" fill="#f8c4d4" opacity=".78"/>
          <circle cx="141" cy="73" r="24" fill="#f48fb1" opacity=".85"/><circle cx="141" cy="62" r="18" fill="#f8c4d4" opacity=".78"/>
          <circle cx="181" cy="70" r="22" fill="#f48fb1" opacity=".82"/><circle cx="181" cy="60" r="17" fill="#f8c4d4" opacity=".75"/>
        </g>
        <!-- Ground cover / grass -->
        <path d="M0,168 Q50,160 100,165 Q150,160 200,165 L200,200 L0,200 Z" fill="#7ab840" opacity=".7"/>
        <!-- Fallen petals -->
        <g fill="#f8c4d4" opacity=".8"><ellipse cx="25" cy="175" rx="5" ry="2.5" transform="rotate(15,25,175)"/><ellipse cx="70" cy="178" rx="4" ry="2" transform="rotate(-20,70,178)"/><ellipse cx="120" cy="172" rx="5" ry="2.5" transform="rotate(28,120,172)"/><ellipse cx="168" cy="176" rx="4" ry="2" transform="rotate(-12,168,176)"/></g>
        <!-- Person with basket -->
        <g fill="#3a1a08" transform="translate(48,165)">
          <ellipse cx="0" cy="-6" rx="5" ry="7"/><circle cx="0" cy="-15" r="5"/>
          <path d="M-5,-5 Q-10,0 -8,5" stroke="#3a1a08" stroke-width="2" fill="none"/>
          <path d="M5,-5 Q8,-2 10,2 Q12,4 10,6" stroke="#3a1a08" stroke-width="2" fill="none"/>
          <ellipse cx="12" cy="7" rx="6" ry="4" fill="#8a5028"/>
        </g>
        <!-- Cherries on branch -->
        <g><circle cx="145" cy="58" r="5" fill="#cc1a20"/><circle cx="155" cy="55" r="5" fill="#cc1a20"/><circle cx="150" cy="62" r="5" fill="#cc1a20"/><path d="M150,52 Q148,44 145,40" stroke="#2a5a10" stroke-width="1.5" fill="none"/><path d="M155,50 Q157,42 155,38" stroke="#2a5a10" stroke-width="1.5" fill="none"/></g>
      </svg>
      <span class="ig-07__cap"><strong>Cherry Orchard</strong><em>Kyoto · Spring bloom</em></span>
      <span class="ig-07__num">07</span>
    </div>

    <!-- 8: Snowy mountain summit -->
    <div class="ig-07__tile" data-idx="7" data-cap="Summit Climb" data-loc="K2 · 8,611 m" data-color="#cdd6e0">
      <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
        <defs><linearGradient id="smg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#1a2a3a"/><stop offset="40%" stop-color="#2a3a50"/><stop offset="100%" stop-color="#3a5060"/></linearGradient><filter id="smf"><feGaussianBlur stdDeviation="3"/></filter></defs>
        <rect width="200" height="200" fill="url(#smg)"/>
        <!-- Stars dense -->
        <g fill="#fff"><circle cx="10" cy="8" r=".9" opacity=".8"/><circle cx="30" cy="4" r="1" opacity=".9"/><circle cx="55" cy="12" r=".8" opacity=".7"/><circle cx="80" cy="5" r="1.1" opacity=".85"/><circle cx="105" cy="9" r=".9" opacity=".8"/><circle cx="130" cy="3" r="1" opacity=".88"/><circle cx="155" cy="11" r=".8" opacity=".75"/><circle cx="178" cy="5" r="1.1" opacity=".85"/><circle cx="195" cy="14" r=".9" opacity=".8"/><circle cx="20" cy="28" r=".8" opacity=".7"/><circle cx="48" cy="22" r="1" opacity=".82"/><circle cx="75" cy="30" r=".9" opacity=".78"/><circle cx="115" cy="24" r=".8" opacity=".8"/><circle cx="160" cy="28" r="1" opacity=".82"/><circle cx="188" cy="22" r=".9" opacity=".78"/></g>
        <!-- Mountain face -->
        <polygon points="0,200 40,100 100,45 160,100 200,200" fill="#c8d8e8"/>
        <polygon points="40,100 100,45 160,100 140,200 60,200" fill="#d8e8f8"/>
        <!-- Summit snow cap -->
        <polygon points="82,80 100,45 118,80 110,88 90,88" fill="#f0f8ff"/>
        <polygon points="85,78 100,48 115,78" fill="#ffffff"/>
        <!-- Crevasse shadows -->
        <g fill="#a0b8cc" opacity=".6"><path d="M55,130 Q65,120 75,132 Q70,142 60,140 Z"/><path d="M130,125 Q140,115 148,127 Q144,137 133,135 Z"/></g>
        <!-- Climber rope line -->
        <path d="M100,45 Q95,80 88,115 Q82,145 78,175" stroke="#ff4020" stroke-width="1.5" fill="none" stroke-dasharray="4,3" opacity=".7"/>
        <!-- Climber at summit -->
        <g transform="translate(100,45)">
          <circle cx="0" cy="-10" r="4" fill="#1a1a2a"/>
          <ellipse cx="0" cy="-3" rx="5" ry="6" fill="#ff4020"/>
          <path d="M-5,-4 Q-10,0 -10,5" stroke="#1a1a2a" stroke-width="2" fill="none"/>
          <path d="M5,-4 Q10,-2 12,3" stroke="#1a1a2a" stroke-width="2" fill="none"/>
          <!-- Flag -->
          <line x1="12" y1="3" x2="12" y2="-12" stroke="#888" stroke-width="1.2"/>
          <polygon points="12,-12 20,-10 12,-6" fill="#ff4020"/>
        </g>
        <!-- Snow ridgeline -->
        <path d="M0,160 Q40,140 100,125 Q160,140 200,150" stroke="#e0ecf8" stroke-width="2" fill="none" opacity=".5"/>
      </svg>
      <span class="ig-07__cap"><strong>Summit Climb</strong><em>K2 · 8,611 m</em></span>
      <span class="ig-07__num">08</span>
    </div>

  </div>

  <footer class="ig-07__statusbar">
    <span>↑ Click a tile to feature</span>
    <span class="ig-07__hint">SVG scale(1.08) on hover · grid-column span 2 on click</span>
    <span>MIT · prefers-reduced-motion ✓</span>
  </footer>

</div>
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
body{background:#0a0a0e;font-family:'DM Sans',sans-serif;padding:1rem;min-height:100vh;display:flex;align-items:center;justify-content:center;color:#fff}
.ig-07{width:100%;max-width:860px;background:#0a0a0e;padding:1rem 1.1rem;display:flex;flex-direction:column;gap:.7rem}
.ig-07__chrome{display:flex;justify-content:space-between;align-items:center;padding-bottom:.55rem;border-bottom:1px solid #1c1c24;flex-wrap:wrap;gap:.5rem}
.ig-07__chrome-l{display:flex;align-items:baseline;gap:1rem;flex-wrap:wrap}
.ig-07__title{font-family:'Playfair Display',serif;font-style:italic;font-weight:700;font-size:1.5rem;color:#fff;letter-spacing:-0.01em}
.ig-07__sub{font-size:.66rem;letter-spacing:.16em;text-transform:uppercase;color:#7a7a8a;font-weight:600}
.ig-07__pill{font-size:.65rem;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:#0a0a0e;background:#fff;padding:.35rem .7rem;border-radius:999px}
.ig-07__grid{display:grid;grid-template-columns:repeat(4,1fr);grid-auto-rows:100px;gap:5px}
.ig-07__tile{overflow:hidden;border-radius:6px;cursor:pointer;position:relative;outline:1px solid rgba(255,255,255,.05);transition:outline-color .3s ease;background:#0a0a0e}
.ig-07__tile:hover{outline-color:var(--accent,#fff);outline-width:2px}
.ig-07__tile svg{width:100%;height:100%;display:block;position:absolute;inset:0;transition:transform .55s cubic-bezier(.25,.46,.45,.94)}
.ig-07__tile:hover svg{transform:scale(1.1)}
.ig-07__tile--active{grid-column:span 2;grid-row:span 2;outline-color:var(--accent,#fff);outline-width:2px}
.ig-07__tile--active svg{transform:scale(1)}
.ig-07__tile--active:hover svg{transform:scale(1.04)}
.ig-07__tile[data-color="#2a9018"]{--accent:#3aa628}
.ig-07__tile[data-color="#88ccdd"]{--accent:#88ccdd}
.ig-07__tile[data-color="#e8a838"]{--accent:#e8a838}
.ig-07__tile[data-color="#2a8aee"]{--accent:#2a8aee}
.ig-07__tile[data-color="#7c6cff"]{--accent:#7c6cff}
.ig-07__tile[data-color="#ff5a32"]{--accent:#ff5a32}
.ig-07__tile[data-color="#ff8ab8"]{--accent:#ff8ab8}
.ig-07__tile[data-color="#cdd6e0"]{--accent:#cdd6e0}
.ig-07__num{position:absolute;top:.45rem;right:.5rem;z-index:3;font-family:'Courier New',monospace;font-size:.6rem;font-weight:700;color:#fff;background:rgba(0,0,0,.55);padding:.18rem .42rem;border-radius:3px;letter-spacing:.1em;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}
.ig-07__cap{position:absolute;left:0;right:0;bottom:0;z-index:3;padding:.55rem .65rem .5rem;display:flex;flex-direction:column;background:linear-gradient(to top,rgba(0,0,0,.92) 0%,rgba(0,0,0,.55) 60%,rgba(0,0,0,0) 100%);pointer-events:none;transform:translateY(0);transition:padding .35s ease}
.ig-07__cap strong{font-size:.72rem;font-weight:700;color:#fff;line-height:1.15;letter-spacing:-0.005em;text-shadow:0 1px 4px rgba(0,0,0,.7)}
.ig-07__cap em{font-style:normal;font-size:.55rem;font-weight:500;color:rgba(255,255,255,.7);letter-spacing:.1em;margin-top:.15rem;opacity:0;max-height:0;transition:opacity .3s ease,max-height .3s ease}
.ig-07__tile--active .ig-07__cap{padding:1rem 1.2rem 1rem}
.ig-07__tile--active .ig-07__cap strong{font-size:1.5rem;line-height:1.1;border-left:3px solid var(--accent,#fff);padding-left:.7rem;margin-bottom:.3rem}
.ig-07__tile--active .ig-07__cap em{opacity:1;max-height:24px;font-size:.66rem;padding-left:.85rem}
.ig-07__tile:not(.ig-07__tile--active):hover .ig-07__cap em{opacity:1;max-height:24px}
.ig-07__statusbar{display:flex;justify-content:space-between;align-items:center;font-size:.6rem;font-weight:500;letter-spacing:.14em;text-transform:uppercase;color:#5a5a6a;padding-top:.5rem;border-top:1px solid #1c1c24;flex-wrap:wrap;gap:.5rem}
.ig-07__hint{color:#9a9aaa;font-family:'Courier New',monospace;letter-spacing:.05em;text-transform:none}
@media(prefers-reduced-motion:reduce){.ig-07__tile svg,.ig-07__tile,.ig-07__cap,.ig-07__cap em{transition:none}.ig-07__tile:hover svg{transform:none}}
(function(){
  const grid=document.getElementById('ig-07-grid');
  const counter=document.getElementById('ig-07-counter');
  if(!grid)return;
  const tiles=Array.from(grid.querySelectorAll('.ig-07__tile'));
  const total=String(tiles.length).padStart(2,'0');
  function syncCounter(){
    if(!counter)return;
    const active=grid.querySelector('.ig-07__tile--active');
    if(!active){counter.textContent='Featured · — / '+total;return;}
    const idx=tiles.indexOf(active)+1;
    const cap=active.getAttribute('data-cap')||'';
    counter.textContent='Featured · '+String(idx).padStart(2,'0')+' / '+total+' · '+cap;
  }
  tiles.forEach(t=>{
    t.addEventListener('click',()=>{
      const active=grid.querySelector('.ig-07__tile--active');
      if(active&&active!==t)active.classList.remove('ig-07__tile--active');
      t.classList.toggle('ig-07__tile--active');
      syncCounter();
    });
  });
  syncCounter();
}());

How this works

The mosaic is a 4-column CSS grid with grid-auto-rows: 100px. One tile carries .ig-07__tile--active at any time, which applies grid-column: span 2; grid-row: span 2 — the grid auto-flow squeezes the other 7 tiles into the remaining cells. A 1px translucent outline turns into a 2px colored outline matching the tile's --accent custom property on hover and active state.

Each tile has three persistent chrome layers on top of the SVG: (1) a monospace section number (01–08) anchored top-right with a backdrop-blur pill; (2) a caption strip at the bottom with a bold title (always visible) over a vignette gradient, and a subtitle line that fades in on hover or active — so you always know what you're looking at without a mystery-tile reveal; (3) on the active tile, the caption strip expands: title jumps to 1.5rem with a colored left-border in the tile's accent color, subtitle becomes permanently visible. Hover triggers an SVG transform: scale(1.1) on inactive tiles (the "zoom" of the name) and a subtle scale(1.04) on the active tile so it has its own zoom personality. The JS hook updates a live counter chip in the header ("Featured · 03 / 08 · Sahara Dunes") every time a tile is clicked.

Customize

  • Change the active span size from 2×2 to 3×2 for a wider hero by editing grid-column: span 3; grid-row: span 2 on .ig-07__tile--active.
  • Swap the per-tile accent palette by editing the eight .ig-07__tile[data-color] rules — the same color drives the outline ring AND the active title's left border.
  • Hide the persistent caption strip and revert to hover-only labels by setting opacity: 0 on .ig-07__cap and adding opacity: 1 on hover/active — useful for image-only mosaics.
  • Change the grid to 3 columns for a chunkier mosaic by editing grid-template-columns: repeat(3,1fr) and bumping grid-auto-rows to 130px to maintain proportions.
  • Add a click-to-reset by editing the JS hook to remove .ig-07__tile--active when the same tile is clicked twice — the current code already does this via classList.toggle.

Watch out for

  • CSS Grid reflow when span changes is not animated — only properties like transform and opacity on child elements can be transitioned. The grid reflow itself will be instant.
  • The aspect-ratio: 1 on non-active tiles conflicts with aspect-ratio: auto needed on the active tile — always remove or override aspect-ratio when applying the span.
  • When the active tile is near the right edge, grid auto-placement may wrap it to the next row rather than spanning in-place. Place tiles that could be activated near the left/center of the grid.

Browser support

ChromeSafariFirefoxEdge
57+ 10.1+ 52+ 57+

CSS Grid is widely supported; aspect-ratio requires Chrome 88+, Safari 15+, Firefox 89+.

Search CodeFronts

Loading…