16 CSS Image Gallery Designs 03 / 16

CSS Infinite Scroll Image Strip

An auto-scrolling horizontal filmstrip of illustrated world photography cards with a mask fade on each edge and JS-powered pause on hover.

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

The code

<div class="ig-03">
  <p class="ig-03__label">Hover to pause · Auto-scrolling world photography</p>
  <div class="ig-03__viewport">
    <div class="ig-03__track" id="ig-03-track">
      <!-- 1: Icelandic waterfall -->
      <div class="ig-03__card">
        <svg viewBox="0 0 220 260" xmlns="http://www.w3.org/2000/svg">
          <defs><linearGradient id="wfg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#2a4a6a"/><stop offset="60%" stop-color="#1a3a5a"/><stop offset="100%" stop-color="#0d2a40"/></linearGradient><filter id="wff"><feGaussianBlur stdDeviation="3"/></filter></defs>
          <rect width="220" height="260" fill="url(#wfg)"/>
          <!-- Cliff faces -->
          <rect x="0" y="0" width="60" height="200" fill="#3a3a2a"/>
          <rect x="160" y="0" width="60" height="220" fill="#2a2a1a"/>
          <!-- Mossy cliff detail -->
          <g fill="#2a4a1a" opacity=".7"><rect x="0" y="40" width="60" height="8"/><rect x="0" y="80" width="58" height="6"/><rect x="0" y="120" width="62" height="7"/><rect x="160" y="55" width="60" height="8"/><rect x="162" y="100" width="58" height="6"/><rect x="158" y="145" width="62" height="7"/></g>
          <!-- Waterfall main -->
          <g filter="url(#wff)">
            <rect x="75" y="0" width="30" height="180" fill="#c8e8f8" opacity=".85"/>
            <rect x="100" y="0" width="20" height="160" fill="#d8f0ff" opacity=".7"/>
            <rect x="68" y="0" width="12" height="140" fill="#b8d8f0" opacity=".6"/>
          </g>
          <!-- Waterfall detail streaks -->
          <g stroke="#e8f8ff" stroke-width="1" fill="none" opacity=".5">
            <line x1="80" y1="0" x2="76" y2="180"/><line x1="90" y1="0" x2="88" y2="175"/>
            <line x1="100" y1="0" x2="99" y2="160"/><line x1="110" y1="0" x2="112" y2="168"/>
            <line x1="120" y1="0" x2="122" y2="155"/>
          </g>
          <!-- Mist pool at base -->
          <ellipse cx="110" cy="185" rx="80" ry="25" fill="#c8e8ff" opacity=".5" filter="url(#wff)"/>
          <ellipse cx="110" cy="195" rx="60" ry="15" fill="#d8f0ff" opacity=".4"/>
          <!-- Pool water -->
          <ellipse cx="110" cy="210" rx="75" ry="22" fill="#2060a0" opacity=".8"/>
          <ellipse cx="110" cy="215" rx="55" ry="14" fill="#3070b0" opacity=".6"/>
          <!-- Rocks -->
          <g fill="#1a1a14">
            <ellipse cx="50" cy="220" rx="18" ry="9"/><ellipse cx="170" cy="218" rx="20" ry="10"/>
            <ellipse cx="100" cy="225" rx="10" ry="6"/><ellipse cx="140" cy="228" rx="12" ry="7"/>
          </g>
          <!-- Rainbow in mist -->
          <path d="M40,190 Q110,150 180,190" stroke="none" fill="none"/>
          <path d="M50,188 Q110,155 170,188" stroke="#ff6060" stroke-width="2" fill="none" opacity=".25"/>
          <path d="M52,191 Q110,158 168,191" stroke="#ff9900" stroke-width="2" fill="none" opacity=".22"/>
          <path d="M54,194 Q110,161 166,194" stroke="#ffee00" stroke-width="2" fill="none" opacity=".22"/>
          <path d="M56,197 Q110,164 164,197" stroke="#00cc44" stroke-width="2" fill="none" opacity=".2"/>
          <path d="M58,200 Q110,167 162,200" stroke="#0088ff" stroke-width="2" fill="none" opacity=".2"/>
          <!-- Fern foliage -->
          <g fill="#1a4a14" opacity=".8">
            <path d="M10,200 Q20,180 30,195 Q20,178 35,172 Q22,175 40,165 Q28,168 42,158"/>
            <path d="M180,195 Q192,175 200,188 Q190,172 208,165 Q194,168 210,158"/>
          </g>
        </svg>
        <div class="ig-03__card-label">Skógafoss · Iceland</div>
      </div>
      <!-- 2: Moroccan spice market -->
      <div class="ig-03__card">
        <svg viewBox="0 0 220 260" xmlns="http://www.w3.org/2000/svg">
          <defs><filter id="sf2"><feGaussianBlur stdDeviation="2"/></filter></defs>
          <rect width="220" height="260" fill="#1a0d05"/>
          <!-- Market stall awning -->
          <g>
            <polygon points="0,0 220,0 220,50 0,50" fill="#c43a10"/>
            <g fill="#e04818">
              <polygon points="0,50 22,35 44,50"/><polygon points="44,50 66,35 88,50"/>
              <polygon points="88,50 110,35 132,50"/><polygon points="132,50 154,35 176,50"/>
              <polygon points="176,50 198,35 220,50"/>
            </g>
          </g>
          <!-- Spice piles -->
          <!-- Saffron/yellow -->
          <ellipse cx="35" cy="135" rx="28" ry="14" fill="#f0c020"/>
          <ellipse cx="35" cy="128" rx="22" ry="10" fill="#f8d040"/>
          <!-- Paprika/red -->
          <ellipse cx="110" cy="130" rx="30" ry="15" fill="#d03020"/>
          <ellipse cx="110" cy="122" rx="24" ry="11" fill="#e04030"/>
          <!-- Turmeric/orange -->
          <ellipse cx="185" cy="133" rx="28" ry="13" fill="#e08020"/>
          <ellipse cx="185" cy="126" rx="22" ry="10" fill="#f09030"/>
          <!-- Cumin/brown -->
          <ellipse cx="60" cy="195" rx="26" ry="13" fill="#6a3a18"/>
          <ellipse cx="60" cy="188" rx="20" ry="9" fill="#8a5028"/>
          <!-- Coriander/green -->
          <ellipse cx="155" cy="192" rx="28" ry="14" fill="#4a8a1a"/>
          <ellipse cx="155" cy="185" rx="22" ry="10" fill="#5aa828"/>
          <!-- Cinnamon sticks jar -->
          <rect x="190" y="172" width="24" height="38" rx="4" fill="#8a4818"/>
          <rect x="192" y="170" width="20" height="8" rx="3" fill="#a05a28"/>
          <g stroke="#6a3010" stroke-width="2" opacity=".8">
            <line x1="194" y1="175" x2="212" y2="175"/><line x1="194" y1="181" x2="212" y2="181"/>
            <line x1="194" y1="187" x2="212" y2="187"/>
          </g>
          <!-- Metal bowls -->
          <g fill="none" stroke="#c0a030" stroke-width="1.5">
            <ellipse cx="35" cy="138" rx="28" ry="7"/><ellipse cx="110" cy="134" rx="30" ry="7"/>
            <ellipse cx="185" cy="137" rx="28" ry="7"/><ellipse cx="60" cy="198" rx="26" ry="7"/>
            <ellipse cx="155" cy="196" rx="28" ry="7"/>
          </g>
          <!-- Price tags -->
          <g fill="#f5e8c0" font-size="7" font-family="sans-serif">
            <rect x="18" y="108" width="24" height="12" rx="2" fill="#f5e8c0"/>
            <text x="22" y="117" fill="#4a1a05">15 DH</text>
            <rect x="94" y="104" width="24" height="12" rx="2" fill="#f5e8c0"/>
            <text x="98" y="113" fill="#4a1a05">20 DH</text>
          </g>
          <!-- Hanging lanterns -->
          <g>
            <line x1="55" y1="50" x2="55" y2="80" stroke="#8a6020" stroke-width="1.5"/>
            <ellipse cx="55" cy="80" rx="12" ry="16" fill="#c04010" opacity=".8"/>
            <ellipse cx="55" cy="80" rx="8" ry="12" fill="#e06020" opacity=".6"/>
            <circle cx="55" cy="80" r="4" fill="#ff8020" opacity=".7" filter="url(#sf2)"/>
            <line x1="150" y1="50" x2="150" y2="72" stroke="#8a6020" stroke-width="1.5"/>
            <ellipse cx="150" cy="72" rx="10" ry="14" fill="#c04010" opacity=".8"/>
            <circle cx="150" cy="72" r="3.5" fill="#ff8020" opacity=".7" filter="url(#sf2)"/>
          </g>
          <!-- Sunbeam from top -->
          <polygon points="80,0 140,0 180,260 140,260" fill="#fff8e0" opacity=".04"/>
        </svg>
        <div class="ig-03__card-label">Spice Souk · Marrakech</div>
      </div>
      <!-- 3: Patagonian peak -->
      <div class="ig-03__card">
        <svg viewBox="0 0 220 260" xmlns="http://www.w3.org/2000/svg">
          <defs><linearGradient id="ptg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#1a3a5c"/><stop offset="40%" stop-color="#2a5a8a"/><stop offset="70%" stop-color="#3a7aaa"/><stop offset="100%" stop-color="#5a9aba"/></linearGradient><filter id="ptf"><feGaussianBlur stdDeviation="4"/></filter></defs>
          <rect width="220" height="260" fill="url(#ptg)"/>
          <!-- Dramatic clouds -->
          <g opacity=".85" filter="url(#ptf)">
            <ellipse cx="60" cy="55" rx="80" ry="35" fill="#c8d8e8"/>
            <ellipse cx="160" cy="42" rx="75" ry="30" fill="#d8e8f8"/>
            <ellipse cx="110" cy="38" rx="60" ry="25" fill="#e8f4ff"/>
          </g>
          <!-- Fitz Roy main peak -->
          <polygon points="80,260 75,180 90,130 100,100 110,70 120,100 130,130 145,180 140,260" fill="#4a4a4a"/>
          <!-- Snow patches on peak -->
          <polygon points="100,100 110,70 120,100 118,108 102,108" fill="#e8f0f8"/>
          <polygon points="90,130 100,100 102,108 95,140" fill="#d8e8f0" opacity=".7"/>
          <polygon points="128,108 120,100 130,130 125,140" fill="#d8e8f0" opacity=".7"/>
          <!-- Adjacent towers -->
          <polygon points="0,260 0,210 20,175 40,210 40,260" fill="#3a3a3a"/>
          <polygon points="175,260 175,195 192,165 210,195 210,260" fill="#3a3a3a"/>
          <polygon points="38,260 36,215 50,183 65,215 65,260" fill="#424242"/>
          <polygon points="155,260 153,200 168,172 183,200 183,260" fill="#424242"/>
          <!-- Rock detail/cracks -->
          <g stroke="#2a2a2a" stroke-width="1" fill="none" opacity=".6">
            <path d="M100,150 Q102,165 98,185"/><path d="M118,145 Q116,160 120,180"/>
            <path d="M108,130 Q110,140 107,155"/>
          </g>
          <!-- Lake in foreground -->
          <path d="M0,230 Q55,212 110,220 Q165,212 220,228 L220,260 L0,260 Z" fill="#2a6080"/>
          <path d="M0,238 Q55,225 110,232 Q165,225 220,236 L220,260 L0,260 Z" fill="#3a7090" opacity=".7"/>
          <!-- Peak reflection in lake -->
          <polygon points="80,260 75,240 100,240 110,252 120,240 145,240 140,260" fill="#3a6a88" opacity=".35"/>
          <!-- Windswept grass -->
          <g stroke="#5a8a2a" stroke-width="1.5" stroke-linecap="round" opacity=".7">
            <path d="M10,230 Q6,218 12,210"/><path d="M25,228 Q21,216 27,208"/>
            <path d="M185,228 Q181,216 187,208"/><path d="M200,232 Q196,220 202,212"/>
            <path d="M210,226 Q206,214 212,206"/>
          </g>
          <!-- Condor silhouette -->
          <g fill="#1a1a1a" transform="translate(155,88)">
            <path d="M0,-5 Q10,-15 20,-8 Q15,0 0,2 Q-15,0 -20,-8 Q-10,-15 0,-5 Z"/>
            <ellipse cx="0" cy="0" rx="5" ry="8"/>
            <path d="M0,8 Q-3,14 -2,18"/>
          </g>
          <!-- Stars visible in sky -->
          <g fill="#fff" opacity=".5"><circle cx="25" cy="18" r=".9"/><circle cx="185" cy="12" r="1"/><circle cx="205" cy="25" r=".8"/></g>
        </svg>
        <div class="ig-03__card-label">Fitz Roy · Patagonia</div>
      </div>
      <!-- 4: Japanese temple gate -->
      <div class="ig-03__card">
        <svg viewBox="0 0 220 260" xmlns="http://www.w3.org/2000/svg">
          <defs><linearGradient id="tpg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#87ceeb"/><stop offset="60%" stop-color="#b0e0ff"/><stop offset="100%" stop-color="#d0ecf8"/></linearGradient><filter id="tpf"><feGaussianBlur stdDeviation="2"/></filter></defs>
          <rect width="220" height="260" fill="url(#tpg)"/>
          <!-- Cherry blossoms sky -->
          <g filter="url(#tpf)" opacity=".5">
            <circle cx="30" cy="40" r="20" fill="#f8c8d8"/><circle cx="60" cy="28" r="25" fill="#fad8e8"/>
            <circle cx="160" cy="35" r="22" fill="#f8c8d8"/><circle cx="195" cy="22" r="18" fill="#fad8e8"/>
            <circle cx="110" cy="20" r="15" fill="#f8d8e8"/>
          </g>
          <!-- Torii gate main -->
          <!-- Base pillars -->
          <rect x="50" y="80" width="18" height="180" fill="#cc2200"/>
          <rect x="152" y="80" width="18" height="180" fill="#cc2200"/>
          <!-- Pillar base rings -->
          <rect x="46" y="240" width="26" height="12" rx="2" fill="#aa1a00"/>
          <rect x="148" y="240" width="26" height="12" rx="2" fill="#aa1a00"/>
          <!-- Top horizontal beams -->
          <rect x="30" y="72" width="160" height="16" rx="4" fill="#cc2200"/>
          <rect x="30" y="82" width="160" height="5" rx="2" fill="#aa1a00" opacity=".6"/>
          <!-- Curved top beam -->
          <path d="M22,60 Q110,38 198,60 L195,72 Q110,50 25,72 Z" fill="#cc2200"/>
          <!-- Second horizontal strut -->
          <rect x="58" y="110" width="104" height="10" rx="3" fill="#aa1a00"/>
          <!-- Gold/metal detail -->
          <g fill="#cc8800" opacity=".8">
            <rect x="55" y="88" width="8" height="12" rx="2"/><rect x="157" y="88" width="8" height="12" rx="2"/>
            <circle cx="110" cy="55" r="6"/><circle cx="40" cy="66" r="4"/><circle cx="180" cy="66" r="4"/>
          </g>
          <!-- Stone path -->
          <g fill="#9a9080">
            <ellipse cx="110" cy="245" rx="22" ry="10"/><ellipse cx="110" cy="225" rx="18" ry="8"/>
            <ellipse cx="110" cy="208" rx="16" ry="7"/>
          </g>
          <!-- Stone lanterns flanking -->
          <g fill="#7a7060">
            <rect x="12" y="185" width="24" height="30" rx="2"/>
            <rect x="8" y="182" width="32" height="6" rx="2"/>
            <rect x="14" y="165" width="20" height="22" rx="1"/>
            <rect x="10" y="162" width="28" height="5" rx="2"/>
            <rect x="184" y="185" width="24" height="30" rx="2"/>
            <rect x="180" y="182" width="32" height="6" rx="2"/>
            <rect x="186" y="165" width="20" height="22" rx="1"/>
            <rect x="182" y="162" width="28" height="5" rx="2"/>
          </g>
          <!-- Lantern glow -->
          <rect x="16" y="168" width="16" height="18" fill="#ffcc40" opacity=".6" filter="url(#tpf)"/>
          <rect x="188" y="168" width="16" height="18" fill="#ffcc40" opacity=".6" filter="url(#tpf)"/>
          <!-- Temple in background -->
          <rect x="75" y="130" width="70" height="50" fill="#8a6a40" opacity=".4"/>
          <polygon points="65,130 110,108 155,130" fill="#6a5030" opacity=".4"/>
          <polygon points="60,128 110,104 160,128" fill="#8a6a40" opacity=".3"/>
          <!-- Falling petals -->
          <g fill="#f8c8d8" opacity=".6">
            <ellipse cx="80" cy="155" rx="4" ry="2.5" transform="rotate(25,80,155)"/>
            <ellipse cx="145" cy="140" rx="3.5" ry="2" transform="rotate(-18,145,140)"/>
            <ellipse cx="25" cy="175" rx="4" ry="2.5" transform="rotate(35,25,175)"/>
            <ellipse cx="192" cy="160" rx="3" ry="2" transform="rotate(-30,192,160)"/>
          </g>
        </svg>
        <div class="ig-03__card-label">Fushimi Inari · Kyoto</div>
      </div>
      <!-- 5: Antelope canyon -->
      <div class="ig-03__card">
        <svg viewBox="0 0 220 260" xmlns="http://www.w3.org/2000/svg">
          <defs>
            <linearGradient id="acg" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stop-color="#c06010"/>
              <stop offset="30%" stop-color="#d07820"/>
              <stop offset="60%" stop-color="#e08830"/>
              <stop offset="100%" stop-color="#b05008"/>
            </linearGradient>
            <linearGradient id="acw" x1="0" y1="0" x2="1" y2="0">
              <stop offset="0%" stop-color="#8a3800"/>
              <stop offset="30%" stop-color="#e06010"/>
              <stop offset="70%" stop-color="#f08020"/>
              <stop offset="100%" stop-color="#a04010"/>
            </linearGradient>
            <filter id="acf"><feGaussianBlur stdDeviation="3"/></filter>
          </defs>
          <rect width="220" height="260" fill="url(#acg)"/>
          <!-- Canyon walls with undulating curves -->
          <!-- Left wall layers -->
          <path d="M0,0 Q25,80 10,160 Q0,220 20,260 L0,260 Z" fill="#6a2800"/>
          <path d="M0,0 Q40,60 22,140 Q8,210 30,260 L0,260 Z" fill="#8a3808"/>
          <path d="M0,0 Q55,50 35,130 Q18,200 40,260 L0,260 Z" fill="#aa4810" opacity=".8"/>
          <!-- Right wall layers -->
          <path d="M220,0 Q195,80 210,160 Q220,220 200,260 L220,260 Z" fill="#6a2800"/>
          <path d="M220,0 Q180,60 198,140 Q212,210 190,260 L220,260 Z" fill="#8a3808"/>
          <path d="M220,0 Q165,50 185,130 Q202,200 180,260 L220,260 Z" fill="#aa4810" opacity=".8"/>
          <!-- Central light beam from top -->
          <path d="M60,0 Q110,20 80,120 Q65,180 70,260 L150,260 Q155,180 140,120 Q110,20 160,0 Z" fill="url(#acw)" opacity=".7"/>
          <!-- Beam of light -->
          <polygon points="85,0 135,0 155,260 65,260" fill="#fff8e0" opacity=".08" filter="url(#acf)"/>
          <polygon points="95,0 125,0 140,260 80,260" fill="#fff8e0" opacity=".06" filter="url(#acf)"/>
          <!-- Sand swirls at bottom -->
          <g fill="#f0c060" opacity=".6">
            <ellipse cx="110" cy="248" rx="55" ry="8" filter="url(#acf)"/>
            <ellipse cx="110" cy="255" rx="40" ry="5"/>
          </g>
          <!-- Wave texture on walls -->
          <g stroke="#c06020" stroke-width="1.5" fill="none" opacity=".4">
            <path d="M18,60 Q35,75 28,95 Q22,110 35,125"/>
            <path d="M22,100 Q38,115 32,135 Q26,150 40,165"/>
            <path d="M202,55 Q186,70 192,90 Q198,108 185,122"/>
            <path d="M198,95 Q183,110 188,130 Q194,148 180,162"/>
          </g>
          <!-- Sky slit at top -->
          <path d="M62,0 L158,0 Q150,15 140,25 Q120,8 100,20 Q80,8 70,22 Q62,14 62,0 Z" fill="#6a9ac8" opacity=".6"/>
          <path d="M75,0 L145,0 Q140,12 130,18 Q110,8 100,15 Q88,8 80,16 Q75,10 75,0 Z" fill="#88b8e0" opacity=".5"/>
        </svg>
        <div class="ig-03__card-label">Antelope Canyon · Arizona</div>
      </div>
      <!-- 6: Tuscany hills -->
      <div class="ig-03__card">
        <svg viewBox="0 0 220 260" xmlns="http://www.w3.org/2000/svg">
          <defs><linearGradient id="tug" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#87b5d8"/><stop offset="40%" stop-color="#b8d4e8"/><stop offset="70%" stop-color="#d4e8c0"/><stop offset="100%" stop-color="#c8d8a0"/></linearGradient></defs>
          <rect width="220" height="260" fill="url(#tug)"/>
          <!-- Rolling hills -->
          <path d="M0,150 Q55,118 110,135 Q165,118 220,138 L220,260 L0,260 Z" fill="#8aaa3a"/>
          <path d="M0,168 Q50,148 100,158 Q155,145 220,162 L220,260 L0,260 Z" fill="#7a9a2a"/>
          <path d="M0,185 Q60,168 120,178 Q175,165 220,180 L220,260 L0,260 Z" fill="#6a8a1a"/>
          <path d="M0,200 Q70,185 140,195 Q185,185 220,195 L220,260 L0,260 Z" fill="#5a7a10"/>
          <!-- Farmhouse -->
          <g fill="#c8a060">
            <rect x="88" y="148" width="44" height="28"/>
            <polygon points="82,148 110,130 138,148" fill="#e8b060"/>
          </g>
          <!-- Roof tiles -->
          <g stroke="#a06030" stroke-width="1" fill="none" opacity=".5">
            <line x1="82" y1="148" x2="138" y2="148"/>
            <line x1="88" y1="142" x2="132" y2="142"/>
            <line x1="94" y1="136" x2="126" y2="136"/>
            <line x1="100" y1="130" x2="120" y2="130"/>
          </g>
          <!-- Windows -->
          <rect x="93" y="154" width="10" height="12" rx="2" fill="#6888aa"/>
          <rect x="117" y="154" width="10" height="12" rx="2" fill="#6888aa"/>
          <!-- Door -->
          <rect x="103" y="163" width="8" height="13" rx="1" fill="#6a4018"/>
          <!-- Cypress trees -->
          <g fill="#2a4a14">
            <ellipse cx="60" cy="148" rx="7" ry="22"/><rect x="57" y="168" width="6" height="12" rx="2"/>
            <ellipse cx="75" cy="152" rx="6" ry="18"/><rect x="72" y="168" width="5" height="12" rx="2"/>
            <ellipse cx="150" cy="150" rx="7" ry="22"/><rect x="147" y="170" width="6" height="10" rx="2"/>
            <ellipse cx="163" cy="153" rx="6" ry="18"/><rect x="160" y="170" width="5" height="10" rx="2"/>
            <ellipse cx="175" cy="148" rx="7" ry="22"/><rect x="172" y="168" width="6" height="12" rx="2"/>
          </g>
          <!-- Winding road -->
          <path d="M0,230 Q55,215 88,200 Q115,188 140,200 Q165,213 220,200" stroke="#c8b080" stroke-width="5" fill="none" opacity=".6"/>
          <!-- Vineyard rows -->
          <g stroke="#4a6a18" stroke-width="1.2" fill="none" opacity=".5">
            <path d="M0,218 Q30,210 60,215"/><path d="M0,224 Q30,216 60,221"/>
            <path d="M0,230 Q25,222 50,227"/><path d="M160,215 Q190,207 220,212"/>
            <path d="M165,222 Q195,214 220,219"/>
          </g>
          <!-- Sunflower field -->
          <g fill="#f0c000" opacity=".7">
            <circle cx="22" cy="210" r="4"/><circle cx="32" cy="206" r="3.5"/><circle cx="12" cy="215" r="3"/>
            <circle cx="42" cy="210" r="4"/><circle cx="52" cy="205" r="3.5"/>
          </g>
          <!-- Olive tree -->
          <g fill="#6a8a28" opacity=".8">
            <circle cx="190" cy="178" r="16"/><circle cx="200" cy="172" r="12"/>
            <circle cx="183" cy="172" r="11"/>
          </g>
          <rect x="192" y="190" width="5" height="20" rx="2" fill="#5a3a18"/>
        </svg>
        <div class="ig-03__card-label">Val d'Orcia · Tuscany</div>
      </div>
      <!-- 7: Maldives overwater bungalow -->
      <div class="ig-03__card">
        <svg viewBox="0 0 220 260" xmlns="http://www.w3.org/2000/svg">
          <defs><linearGradient id="mlg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#0088cc"/><stop offset="35%" stop-color="#00aadd"/><stop offset="60%" stop-color="#20ccee"/><stop offset="100%" stop-color="#40ddee"/></linearGradient><filter id="mlf"><feGaussianBlur stdDeviation="3"/></filter></defs>
          <rect width="220" height="260" fill="url(#mlg)"/>
          <!-- Sky gradient top -->
          <rect width="220" height="100" fill="#4499cc" opacity=".4"/>
          <rect width="220" height="60" fill="#55aad4" opacity=".3"/>
          <!-- Horizon line / sea edge -->
          <rect x="0" y="98" width="220" height="2" fill="#0066aa" opacity=".3"/>
          <!-- Water reflections -->
          <g stroke="#40ccee" stroke-width=".8" fill="none" opacity=".3">
            <path d="M0,115 Q55,108 110,115 Q165,108 220,115"/>
            <path d="M0,128 Q50,122 110,128 Q170,122 220,128"/>
            <path d="M0,142 Q55,136 110,142 Q165,136 220,142"/>
            <path d="M0,158 Q50,152 110,158 Q170,152 220,158"/>
          </g>
          <!-- Dock/pier -->
          <rect x="95" y="95" width="30" height="165" fill="#8a6a3a" opacity=".9"/>
          <g stroke="#7a5a2a" stroke-width="1" fill="none" opacity=".5">
            <line x1="95" y1="110" x2="125" y2="110"/><line x1="95" y1="125" x2="125" y2="125"/>
            <line x1="95" y1="140" x2="125" y2="140"/><line x1="95" y1="155" x2="125" y2="155"/>
            <line x1="95" y1="170" x2="125" y2="170"/><line x1="95" y1="185" x2="125" y2="185"/>
          </g>
          <!-- Bungalow base -->
          <rect x="68" y="60" width="84" height="55" fill="#e8d4a0" rx="3"/>
          <!-- Thatched roof -->
          <path d="M55,65 Q110,35 165,65 L158,72 Q110,44 62,72 Z" fill="#a07830"/>
          <path d="M60,68 Q110,40 160,68 L154,74 Q110,46 66,74 Z" fill="#c09040" opacity=".7"/>
          <!-- Roof thatch lines -->
          <g stroke="#8a6020" stroke-width=".8" fill="none" opacity=".5">
            <line x1="80" y1="55" x2="78" y2="68"/><line x1="90" y1="48" x2="88" y2="65"/>
            <line x1="100" y1="44" x2="100" y2="63"/><line x1="110" y1="42" x2="110" y2="63"/>
            <line x1="120" y1="44" x2="122" y2="63"/><line x1="130" y1="48" x2="132" y2="65"/>
            <line x1="140" y1="55" x2="142" y2="68"/>
          </g>
          <!-- Windows -->
          <rect x="76" y="72" width="18" height="22" rx="2" fill="#88c8e8"/>
          <rect x="126" y="72" width="18" height="22" rx="2" fill="#88c8e8"/>
          <!-- Door -->
          <rect x="100" y="80" width="20" height="35" rx="2" fill="#6a4a20"/>
          <!-- Deck/balcony -->
          <rect x="62" y="105" width="96" height="12" rx="2" fill="#c8a860"/>
          <g fill="#a08840">
            <rect x="65" y="102" width="4" height="15"/><rect x="80" y="102" width="4" height="15"/>
            <rect x="95" y="102" width="4" height="15"/><rect x="110" y="102" width="4" height="15"/>
            <rect x="125" y="102" width="4" height="15"/><rect x="140" y="102" width="4" height="15"/>
            <rect x="152" y="102" width="4" height="15"/>
          </g>
          <!-- Palm tree on deck -->
          <g transform="translate(55,105)">
            <line x1="0" y1="0" x2="-5" y2="-35" stroke="#6a4a1a" stroke-width="3" stroke-linecap="round"/>
            <g fill="#2a6a18" opacity=".9">
              <ellipse cx="-12" cy="-38" rx="14" ry="5" transform="rotate(-30,-12,-38)"/>
              <ellipse cx="-3" cy="-42" rx="14" ry="5" transform="rotate(15,-3,-42)"/>
              <ellipse cx="-18" cy="-30" rx="12" ry="4" transform="rotate(-50,-18,-30)"/>
            </g>
          </g>
          <!-- Sea fish visible through clear water -->
          <g fill="#0055aa" opacity=".3">
            <ellipse cx="40" cy="180" rx="8" ry="4"/><ellipse cx="60" cy="195" rx="6" ry="3"/>
            <ellipse cx="175" cy="185" rx="7" ry="3.5"/><ellipse cx="195" cy="200" rx="8" ry="4"/>
          </g>
          <!-- Sky clouds -->
          <g fill="#fff" opacity=".6" filter="url(#mlf)">
            <ellipse cx="35" cy="30" rx="30" ry="12"/><ellipse cx="180" cy="22" rx="28" ry="10"/>
          </g>
        </svg>
        <div class="ig-03__card-label">Overwater Bungalow · Maldives</div>
      </div>
      <!-- 8: Norwegian fjord -->
      <div class="ig-03__card">
        <svg viewBox="0 0 220 260" xmlns="http://www.w3.org/2000/svg">
          <defs><linearGradient id="fjg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#4a88b8"/><stop offset="40%" stop-color="#6aaad4"/><stop offset="60%" stop-color="#2a6088"/><stop offset="100%" stop-color="#1a4a6a"/></linearGradient><filter id="fjf"><feGaussianBlur stdDeviation="3"/></filter></defs>
          <rect width="220" height="260" fill="url(#fjg)"/>
          <!-- Dramatic cliff walls -->
          <polygon points="0,0 0,260 55,220 35,140 50,80 30,0" fill="#2a3a2a"/>
          <polygon points="0,0 40,0 55,80 38,150 58,225 0,260" fill="#3a4a3a"/>
          <polygon points="220,0 220,260 165,218 185,138 170,78 190,0" fill="#2a3a2a"/>
          <polygon points="220,0 180,0 165,78 182,148 162,222 220,260" fill="#3a4a3a"/>
          <!-- Waterfall on cliff -->
          <path d="M32,40 Q30,90 28,140 Q26,180 30,220" stroke="#c8e8f8" stroke-width="3" fill="none" opacity=".6" filter="url(#fjf)"/>
          <path d="M35,40 Q33,85 32,135 Q30,175 34,218" stroke="#d8f0ff" stroke-width="2" fill="none" opacity=".5"/>
          <!-- Snow on cliff tops -->
          <polygon points="0,0 40,0 38,22 20,18 0,14" fill="#e8f0f8" opacity=".8"/>
          <polygon points="180,0 220,0 220,18 200,22 182,18" fill="#e8f0f8" opacity=".8"/>
          <!-- Green on cliffs (vegetation) -->
          <g fill="#2a5a1a" opacity=".6">
            <ellipse cx="30" cy="90" rx="20" ry="12"/><ellipse cx="15" cy="110" rx="18" ry="10"/>
            <ellipse cx="195" cy="88" rx="18" ry="11"/><ellipse cx="208" cy="112" rx="16" ry="9"/>
          </g>
          <!-- Fjord water -->
          <path d="M50,130 Q110,118 170,130 L175,260 L45,260 Z" fill="#1a4a6a" opacity=".9"/>
          <path d="M55,140 Q110,130 165,140 L168,260 L52,260 Z" fill="#2a5a7a" opacity=".8"/>
          <!-- Water reflections -->
          <g stroke="#3a7a9a" stroke-width="1" fill="none" opacity=".4">
            <path d="M55,160 Q110,152 165,160"/><path d="M57,175 Q110,167 163,175"/>
            <path d="M60,190 Q110,182 160,190"/><path d="M62,208 Q110,200 158,208"/>
          </g>
          <!-- Cliff reflection in water -->
          <polygon points="50,130 55,260 0,260 0,130" fill="#2a3a2a" opacity=".2"/>
          <polygon points="170,130 165,260 220,260 220,130" fill="#2a3a2a" opacity=".2"/>
          <!-- Small village at base -->
          <g fill="#c8a860" opacity=".9">
            <rect x="62" y="215" width="22" height="18"/><polygon points="57,215 73,202 89,215" fill="#e8c070"/>
            <rect x="92" y="218" width="18" height="15"/><polygon points="88,218 101,207 114,218" fill="#e8c070"/>
            <rect x="118" y="215" width="20" height="18"/><polygon points="114,215 128,202 142,215" fill="#e8c070"/>
          </g>
          <!-- Boat on fjord -->
          <g transform="translate(100,200)">
            <path d="M-18,0 Q0,-4 18,0 Q14,8 -14,8 Z" fill="#d04020"/>
            <rect x="-2" y="-14" width="3" height="14" fill="#8a6030"/>
            <polygon points="-1,-14 -1,-4 8,-8" fill="#e8f4ff" opacity=".7"/>
          </g>
          <!-- Cloud reflections -->
          <g fill="#fff" opacity=".3" filter="url(#fjf)">
            <ellipse cx="110" cy="148" rx="30" ry="8"/>
          </g>
          <!-- Sky clouds -->
          <g fill="#fff" opacity=".7" filter="url(#fjf)">
            <ellipse cx="80" cy="25" rx="38" ry="14"/><ellipse cx="165" cy="18" rx="30" ry="11"/>
          </g>
        </svg>
        <div class="ig-03__card-label">Nærøyfjord · Norway</div>
      </div>
    </div>
  </div>
</div>
<script>
(function(){
  const track=document.getElementById('ig-03-track');
  const wrapper=track&&track.closest('.ig-03');
  if(!track||!wrapper)return;
  Array.from(track.children).forEach(el=>track.appendChild(el.cloneNode(true)));
  wrapper.addEventListener('mouseenter',()=>wrapper.style.setProperty('--play','paused'));
  wrapper.addEventListener('mouseleave',()=>wrapper.style.setProperty('--play','running'));
}());
</script>
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
body{background:#0a0a12;font-family:'DM Sans',sans-serif;padding:3rem 0;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;gap:2rem}
.ig-03{--play:running;width:100%}
.ig-03__label{color:rgba(255,255,255,.35);font-size:.72rem;letter-spacing:.14em;text-transform:uppercase;text-align:center;margin-bottom:1.2rem}
.ig-03__viewport{overflow:hidden;mask-image:linear-gradient(to right,transparent,black 8%,black 92%,transparent);-webkit-mask-image:linear-gradient(to right,transparent,black 8%,black 92%,transparent)}
.ig-03__track{display:flex;gap:1.2rem;width:max-content;animation:ig-03-scroll 24s linear infinite;animation-play-state:var(--play);will-change:transform}
.ig-03__card{flex-shrink:0;width:220px;height:260px;border-radius:16px;overflow:hidden;position:relative;box-shadow:0 8px 30px rgba(0,0,0,.4)}
.ig-03__card svg{width:100%;height:100%;display:block}
.ig-03__card-label{position:absolute;bottom:0;left:0;right:0;padding:.9rem 1rem;background:linear-gradient(to top,rgba(0,0,0,.8),transparent);color:#fff;font-size:.78rem;font-weight:600;letter-spacing:.04em}
@keyframes ig-03-scroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@media(prefers-reduced-motion:reduce){.ig-03__track{animation:none}}
(function(){
  const track=document.getElementById('ig-03-track');
  const wrapper=track&&track.closest('.ig-03');
  if(!track||!wrapper)return;
  Array.from(track.children).forEach(el=>track.appendChild(el.cloneNode(true)));
  wrapper.addEventListener('mouseenter',()=>wrapper.style.setProperty('--play','paused'));
  wrapper.addEventListener('mouseleave',()=>wrapper.style.setProperty('--play','running'));
}());

How this works

The track element contains duplicated card sets so the scroll wraps seamlessly. A @keyframes ig-03-scroll moves the track from translateX(0) to translateX(-50%) — because both halves are identical, the loop is invisible. The animation-play-state CSS custom property --play controls play/pause and is toggled between running and paused via JavaScript on mouse events.

Edge fade uses mask-image: linear-gradient(to right, transparent, black 8%, black 92%, transparent) on the viewport element — a single CSS property that gracefully hides the beginning and end of the strip without clipping layout.

Customize

  • Adjust scroll speed by changing the 24s duration on .ig-03__track — halve it for a more energetic strip, double for a slow ambient flow.
  • Change the edge fade width by editing 8% and 92% in the mask-image gradient — try 15% and 85% for a stronger vignette.
  • Reverse scroll direction by changing translateX(-50%) to translateX(50%) in the keyframe (also swap from/to order).
  • Add a second row strip scrolling in reverse by duplicating .ig-03__viewport and adding animation-direction: reverse to its track.
  • Card width is set by min-width: clamp(220px,58vw,310px) — editing the middle value adjusts how many cards are visible at once on different screens.

Watch out for

  • The JS duplication step must run before the animation starts, otherwise the seamless loop will show a visible gap at the halfway point. Use a DOMContentLoaded or inline script.
  • will-change: transform on the track element promotes it to its own compositor layer, preventing scroll jank — but creates a new stacking context, so z-index on child elements behaves differently.
  • On some mobile browsers, mask-image requires the -webkit- prefix. Always include both mask-image and -webkit-mask-image declarations.

Browser support

ChromeSafariFirefoxEdge
4+ 3.1+ 3.6+ 4+

CSS mask-image requires -webkit- prefix on Safari; animation-play-state broadly supported.

Search CodeFronts

Loading…