16 CSS Image Gallery Designs 04 / 16
CSS Polaroid Stack Gallery
Five vintage-style polaroid photos stacked with random rotations that fan out on hover, with a flip animation triggered on click.
The code
<div class="ig-04">
<div class="ig-04__scene">
<div class="ig-04__stack" id="ig-04-stack">
<!-- Polaroid 1: Venice canal -->
<div class="ig-04__card">
<div class="ig-04__photo">
<svg viewBox="0 0 150 150" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="vg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#6aaad4"/><stop offset="50%" stop-color="#3a88b8"/><stop offset="100%" stop-color="#2a6090"/></linearGradient></defs>
<rect width="150" height="150" fill="url(#vg)"/>
<!-- Buildings -->
<g fill="#c8a870"><rect x="5" y="60" width="25" height="90"/><rect x="33" y="45" width="22" height="105"/><rect x="58" y="55" width="20" height="95"/><rect x="82" y="40" width="28" height="110"/><rect x="113" y="52" width="24" height="98"/></g>
<g fill="#b89858"><rect x="5" y="60" width="25" height="10"/><rect x="33" y="45" width="22" height="10"/><rect x="58" y="55" width="20" height="10"/><rect x="82" y="40" width="28" height="10"/><rect x="113" y="52" width="24" height="10"/></g>
<!-- Windows -->
<g fill="#6888aa" opacity=".7"><rect x="10" y="72" width="6" height="8"/><rect x="20" y="72" width="6" height="8"/><rect x="38" y="58" width="6" height="8"/><rect x="48" y="58" width="6" height="8"/><rect x="88" y="52" width="7" height="10"/><rect x="100" y="52" width="7" height="10"/><rect x="118" y="62" width="7" height="8"/></g>
<!-- Canal water -->
<path d="M0,118 Q38,110 75,118 Q112,110 150,118 L150,150 L0,150 Z" fill="#1a4a7a"/>
<!-- Gondola -->
<g transform="translate(50,128)">
<path d="M-25,0 Q-20,-6 0,-5 Q20,-6 28,0 Q22,7 -20,7 Z" fill="#0a0a0a"/>
<rect x="-5" y="-15" width="2.5" height="15" fill="#8a6030"/>
<ellipse cx="-4" cy="-16" rx="8" ry="4" fill="#c8a040" opacity=".7"/>
<ellipse cx="-4" cy="-17" rx="5" ry="3" fill="#e8c060"/>
<!-- Gondolier -->
<ellipse cx="10" cy="-8" rx="4" ry="5" fill="#1a1a2a"/>
<circle cx="10" cy="-14" r="3.5" fill="#c8a880"/>
</g>
<!-- Reflection -->
<g opacity=".3"><rect x="5" y="120" width="25" height="30" fill="#c8a870"/><rect x="33" y="115" width="22" height="35" fill="#c8a870"/></g>
</svg>
</div>
<div class="ig-04__meta">Venice, 1998</div>
</div>
<!-- Polaroid 2: Taj Mahal -->
<div class="ig-04__card">
<div class="ig-04__photo">
<svg viewBox="0 0 150 150" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="tmg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#f5e8d8"/><stop offset="50%" stop-color="#e8d0b0"/><stop offset="100%" stop-color="#d4b890"/></linearGradient></defs>
<rect width="150" height="150" fill="#c8e8f8"/>
<!-- Sky -->
<rect width="150" height="80" fill="#a8d8f0"/>
<!-- Main dome -->
<ellipse cx="75" cy="52" rx="22" ry="28" fill="#f5f0e8"/>
<!-- Finial -->
<rect x="73" y="24" width="4" height="14" fill="#d0c0a0"/>
<circle cx="75" cy="22" r="4" fill="#c8b090"/>
<!-- Main building -->
<rect x="35" y="72" width="80" height="55" fill="#f0ece0"/>
<!-- Arched entrance -->
<path d="M65,127 Q65,98 75,95 Q85,98 85,127" fill="#d8ccc0"/>
<ellipse cx="75" cy="95" rx="12" ry="5" fill="#d8ccc0"/>
<!-- Side minarets -->
<rect x="22" y="90" width="10" height="55" fill="#f0ece0"/>
<ellipse cx="27" cy="87" rx="6" ry="10" fill="#f0ece0"/>
<circle cx="27" cy="79" r="3" fill="#c8b090"/>
<rect x="118" y="90" width="10" height="55" fill="#f0ece0"/>
<ellipse cx="123" cy="87" rx="6" ry="10" fill="#f0ece0"/>
<circle cx="123" cy="79" r="3" fill="#c8b090"/>
<!-- Reflecting pool -->
<rect x="55" y="127" width="40" height="23" fill="#88c8e8" opacity=".7"/>
<!-- Marble inlay pattern -->
<g fill="none" stroke="#d0c0a0" stroke-width=".8" opacity=".5">
<rect x="40" y="76" width="70" height="50"/>
<line x1="75" y1="76" x2="75" y2="126"/>
</g>
</svg>
</div>
<div class="ig-04__meta">Taj Mahal, 2002</div>
</div>
<!-- Polaroid 3: Santorini sunset -->
<div class="ig-04__card">
<div class="ig-04__photo">
<svg viewBox="0 0 150 150" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="ssg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#ff6b35"/><stop offset="40%" stop-color="#ff9850"/><stop offset="70%" stop-color="#ffcc80"/><stop offset="100%" stop-color="#2266cc"/></linearGradient></defs>
<rect width="150" height="150" fill="url(#ssg)"/>
<circle cx="75" cy="60" r="18" fill="#fff5a0" opacity=".9"/>
<!-- Whitewashed buildings -->
<g fill="#f5f5f0">
<rect x="0" y="90" width="30" height="60"/><rect x="28" y="80" width="28" height="70"/>
<rect x="54" y="88" width="25" height="62"/><rect x="78" y="75" width="30" height="75"/>
<rect x="107" y="85" width="28" height="65"/><rect x="133" y="92" width="17" height="58"/>
</g>
<!-- Blue domes -->
<g fill="#2266cc">
<ellipse cx="42" cy="78" rx="14" ry="10"/><ellipse cx="93" cy="73" rx="16" ry="12"/>
</g>
<!-- Windows, blue detail -->
<g fill="#4488ee">
<rect x="5" y="100" width="8" height="10" rx="2"/><rect x="18" y="100" width="8" height="10" rx="2"/>
<rect x="33" y="88" width="8" height="10" rx="2"/><rect x="83" y="84" width="8" height="10" rx="2"/>
</g>
<!-- Sea -->
<rect x="0" y="140" width="150" height="10" fill="#1144aa" opacity=".6"/>
</svg>
</div>
<div class="ig-04__meta">Santorini, 2005</div>
</div>
<!-- Polaroid 4: Safari elephant -->
<div class="ig-04__card">
<div class="ig-04__photo">
<svg viewBox="0 0 150 150" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="sfg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#e8c870"/><stop offset="50%" stop-color="#d4a840"/><stop offset="100%" stop-color="#8a5010"/></linearGradient></defs>
<rect width="150" height="150" fill="url(#sfg)"/>
<!-- Acacia tree -->
<rect x="110" y="80" width="6" height="70" fill="#4a2808"/>
<ellipse cx="113" cy="78" rx="28" ry="12" fill="#2a5a14"/>
<ellipse cx="100" cy="72" rx="20" ry="9" fill="#3a7020"/>
<ellipse cx="126" cy="70" rx="18" ry="8" fill="#2a5a14"/>
<!-- Elephant -->
<g transform="translate(20,80)" fill="#7a7060">
<ellipse cx="40" cy="30" rx="38" ry="26"/> <!-- body -->
<ellipse cx="14" cy="22" rx="18" ry="20"/> <!-- head -->
<!-- Ear -->
<ellipse cx="2" cy="18" rx="12" ry="15" fill="#8a8070"/>
<!-- Trunk -->
<path d="M5,32 Q-5,42 -2,58 Q0,65 5,60 Q8,52 5,42 Q12,38 10,32" fill="#7a7060"/>
<!-- Legs -->
<rect x="10" y="50" width="12" height="30" rx="5" fill="#6a6050"/>
<rect x="26" y="50" width="12" height="32" rx="5" fill="#6a6050"/>
<rect x="44" y="50" width="12" height="30" rx="5" fill="#6a6050"/>
<rect x="60" y="50" width="12" height="28" rx="5" fill="#6a6050"/>
<!-- Tusk -->
<path d="M8,36 Q-2,44 -5,52" stroke="#f0e8c0" stroke-width="3" fill="none" stroke-linecap="round"/>
<!-- Eye -->
<circle cx="8" cy="16" r="3" fill="#1a1a14"/>
<circle cx="9" cy="15" r="1" fill="#fff"/>
<!-- Tail -->
<path d="M78,32 Q88,35 90,44" stroke="#6a6050" stroke-width="2.5" fill="none"/>
</g>
<!-- Baby elephant -->
<g transform="translate(72,100)" fill="#8a8070" opacity=".85">
<ellipse cx="18" cy="16" rx="18" ry="13"/>
<ellipse cx="5" cy="11" rx="9" ry="10"/>
<path d="M2,15 Q-4,22 -2,30 Q0,34 3,30 Q5,24 2,18" fill="#8a8070"/>
<rect x="4" y="25" width="6" height="14" rx="3" fill="#7a7060"/>
<rect x="13" y="25" width="6" height="14" rx="3" fill="#7a7060"/>
<rect x="22" y="25" width="6" height="14" rx="3" fill="#7a7060"/>
<rect x="30" y="25" width="6" height="12" rx="3" fill="#7a7060"/>
</g>
<!-- Grass tufts -->
<g stroke="#6a8a28" stroke-width="1.5" stroke-linecap="round" fill="none" opacity=".7">
<line x1="5" y1="148" x2="3" y2="135"/><line x1="10" y1="148" x2="12" y2="136"/>
<line x1="145" y1="148" x2="143" y2="134"/><line x1="138" y1="148" x2="140" y2="136"/>
</g>
</svg>
</div>
<div class="ig-04__meta">Amboseli, 2010</div>
</div>
<!-- Polaroid 5: Northern Lights cabin -->
<div class="ig-04__card">
<div class="ig-04__photo">
<svg viewBox="0 0 150 150" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="nlg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#040820"/><stop offset="50%" stop-color="#0a1535"/><stop offset="100%" stop-color="#0d1e25"/></linearGradient><filter id="nlf"><feGaussianBlur stdDeviation="4"/></filter></defs>
<rect width="150" height="150" fill="url(#nlg)"/>
<g opacity=".7" filter="url(#nlf)">
<path d="M0,45 Q38,22 75,45 Q112,62 150,32" stroke="#00e8a0" stroke-width="18" fill="none"/>
<path d="M0,62 Q40,38 80,56 Q118,75 150,48" stroke="#6040ff" stroke-width="12" fill="none"/>
<path d="M0,35 Q30,18 60,38 Q95,58 150,26" stroke="#00c8e0" stroke-width="8" fill="none"/>
</g>
<g fill="#fff" opacity=".7"><circle cx="10" cy="8" r=".8"/><circle cx="30" cy="4" r="1"/><circle cx="60" cy="10" r=".9"/><circle cx="90" cy="5" r="1.1"/><circle cx="120" cy="8" r=".8"/><circle cx="145" cy="4" r="1"/></g>
<!-- Snow ground -->
<rect x="0" y="115" width="150" height="35" fill="#c8d8e8"/>
<path d="M0,112 Q38,104 75,110 Q112,104 150,108 L150,115 L0,115 Z" fill="#d8e8f5"/>
<!-- Cabin -->
<g fill="#2a1808"><rect x="50" y="95" width="50" height="28"/><polygon points="44,95 75,78 106,95"/></g>
<rect x="55" y="100" width="12" height="12" fill="#ff9040" opacity=".9"/>
<rect x="83" y="100" width="12" height="12" fill="#ff9040" opacity=".9"/>
<rect x="67" y="105" width="10" height="18" fill="#5a2808"/>
<!-- Window glow -->
<rect x="55" y="100" width="12" height="12" fill="#ffaa60" opacity=".4" filter="url(#nlf)"/>
<rect x="83" y="100" width="12" height="12" fill="#ffaa60" opacity=".4" filter="url(#nlf)"/>
<!-- Pine trees -->
<g fill="#0d1e10">
<polygon points="15,115 22,88 29,115"/><polygon points="18,100 22,88 26,100"/>
<polygon points="125,115 132,92 139,115"/><polygon points="128,102 132,92 136,102"/>
</g>
<!-- Chimney smoke -->
<path d="M76,78 Q74,68 78,58 Q80,48 76,40" stroke="#888" stroke-width="3" fill="none" opacity=".5" stroke-linecap="round"/>
</svg>
</div>
<div class="ig-04__meta">Lapland, 2015</div>
</div>
</div>
</div>
<p class="ig-04__hint">Hover to fan · Click to flip</p>
</div>
<script>
(function(){
const stack=document.getElementById('ig-04-stack');
if(!stack)return;
const cards=Array.from(stack.querySelectorAll('.ig-04__card'));
const rots=cards.map(()=>(Math.random()-.5)*16);
cards.forEach((c,i)=>{c.style.transform=`rotate(${rots[i]}deg)`;c.style.zIndex=i;});
stack.addEventListener('mouseenter',()=>{
const spread=20,total=cards.length;
cards.forEach((c,i)=>{
const angle=-spread/2+(spread/(total-1))*i;
const tx=-130+(260/(total-1))*i;
c.style.transform=`translateX(${tx}px) rotate(${angle}deg)`;
c.style.setProperty('--ft',`translateX(${tx}px) rotate(${angle}deg)`);
});
});
stack.addEventListener('mouseleave',()=>{
cards.forEach((c,i)=>{c.style.transform=`rotate(${rots[i]}deg)`;c.classList.remove('ig-04--active');});
});
cards.forEach((c,i)=>{
c.addEventListener('click',()=>{
cards.forEach((x,j)=>{x.classList.remove('ig-04--active');x.style.zIndex=j;});
c.classList.add('ig-04--active');c.style.zIndex=cards.length+1;
c.addEventListener('animationend',()=>c.classList.remove('ig-04--active'),{once:true});
});
});
}());
</script> <div class="ig-04">
<div class="ig-04__scene">
<div class="ig-04__stack" id="ig-04-stack">
<!-- Polaroid 1: Venice canal -->
<div class="ig-04__card">
<div class="ig-04__photo">
<svg viewBox="0 0 150 150" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="vg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#6aaad4"/><stop offset="50%" stop-color="#3a88b8"/><stop offset="100%" stop-color="#2a6090"/></linearGradient></defs>
<rect width="150" height="150" fill="url(#vg)"/>
<!-- Buildings -->
<g fill="#c8a870"><rect x="5" y="60" width="25" height="90"/><rect x="33" y="45" width="22" height="105"/><rect x="58" y="55" width="20" height="95"/><rect x="82" y="40" width="28" height="110"/><rect x="113" y="52" width="24" height="98"/></g>
<g fill="#b89858"><rect x="5" y="60" width="25" height="10"/><rect x="33" y="45" width="22" height="10"/><rect x="58" y="55" width="20" height="10"/><rect x="82" y="40" width="28" height="10"/><rect x="113" y="52" width="24" height="10"/></g>
<!-- Windows -->
<g fill="#6888aa" opacity=".7"><rect x="10" y="72" width="6" height="8"/><rect x="20" y="72" width="6" height="8"/><rect x="38" y="58" width="6" height="8"/><rect x="48" y="58" width="6" height="8"/><rect x="88" y="52" width="7" height="10"/><rect x="100" y="52" width="7" height="10"/><rect x="118" y="62" width="7" height="8"/></g>
<!-- Canal water -->
<path d="M0,118 Q38,110 75,118 Q112,110 150,118 L150,150 L0,150 Z" fill="#1a4a7a"/>
<!-- Gondola -->
<g transform="translate(50,128)">
<path d="M-25,0 Q-20,-6 0,-5 Q20,-6 28,0 Q22,7 -20,7 Z" fill="#0a0a0a"/>
<rect x="-5" y="-15" width="2.5" height="15" fill="#8a6030"/>
<ellipse cx="-4" cy="-16" rx="8" ry="4" fill="#c8a040" opacity=".7"/>
<ellipse cx="-4" cy="-17" rx="5" ry="3" fill="#e8c060"/>
<!-- Gondolier -->
<ellipse cx="10" cy="-8" rx="4" ry="5" fill="#1a1a2a"/>
<circle cx="10" cy="-14" r="3.5" fill="#c8a880"/>
</g>
<!-- Reflection -->
<g opacity=".3"><rect x="5" y="120" width="25" height="30" fill="#c8a870"/><rect x="33" y="115" width="22" height="35" fill="#c8a870"/></g>
</svg>
</div>
<div class="ig-04__meta">Venice, 1998</div>
</div>
<!-- Polaroid 2: Taj Mahal -->
<div class="ig-04__card">
<div class="ig-04__photo">
<svg viewBox="0 0 150 150" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="tmg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#f5e8d8"/><stop offset="50%" stop-color="#e8d0b0"/><stop offset="100%" stop-color="#d4b890"/></linearGradient></defs>
<rect width="150" height="150" fill="#c8e8f8"/>
<!-- Sky -->
<rect width="150" height="80" fill="#a8d8f0"/>
<!-- Main dome -->
<ellipse cx="75" cy="52" rx="22" ry="28" fill="#f5f0e8"/>
<!-- Finial -->
<rect x="73" y="24" width="4" height="14" fill="#d0c0a0"/>
<circle cx="75" cy="22" r="4" fill="#c8b090"/>
<!-- Main building -->
<rect x="35" y="72" width="80" height="55" fill="#f0ece0"/>
<!-- Arched entrance -->
<path d="M65,127 Q65,98 75,95 Q85,98 85,127" fill="#d8ccc0"/>
<ellipse cx="75" cy="95" rx="12" ry="5" fill="#d8ccc0"/>
<!-- Side minarets -->
<rect x="22" y="90" width="10" height="55" fill="#f0ece0"/>
<ellipse cx="27" cy="87" rx="6" ry="10" fill="#f0ece0"/>
<circle cx="27" cy="79" r="3" fill="#c8b090"/>
<rect x="118" y="90" width="10" height="55" fill="#f0ece0"/>
<ellipse cx="123" cy="87" rx="6" ry="10" fill="#f0ece0"/>
<circle cx="123" cy="79" r="3" fill="#c8b090"/>
<!-- Reflecting pool -->
<rect x="55" y="127" width="40" height="23" fill="#88c8e8" opacity=".7"/>
<!-- Marble inlay pattern -->
<g fill="none" stroke="#d0c0a0" stroke-width=".8" opacity=".5">
<rect x="40" y="76" width="70" height="50"/>
<line x1="75" y1="76" x2="75" y2="126"/>
</g>
</svg>
</div>
<div class="ig-04__meta">Taj Mahal, 2002</div>
</div>
<!-- Polaroid 3: Santorini sunset -->
<div class="ig-04__card">
<div class="ig-04__photo">
<svg viewBox="0 0 150 150" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="ssg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#ff6b35"/><stop offset="40%" stop-color="#ff9850"/><stop offset="70%" stop-color="#ffcc80"/><stop offset="100%" stop-color="#2266cc"/></linearGradient></defs>
<rect width="150" height="150" fill="url(#ssg)"/>
<circle cx="75" cy="60" r="18" fill="#fff5a0" opacity=".9"/>
<!-- Whitewashed buildings -->
<g fill="#f5f5f0">
<rect x="0" y="90" width="30" height="60"/><rect x="28" y="80" width="28" height="70"/>
<rect x="54" y="88" width="25" height="62"/><rect x="78" y="75" width="30" height="75"/>
<rect x="107" y="85" width="28" height="65"/><rect x="133" y="92" width="17" height="58"/>
</g>
<!-- Blue domes -->
<g fill="#2266cc">
<ellipse cx="42" cy="78" rx="14" ry="10"/><ellipse cx="93" cy="73" rx="16" ry="12"/>
</g>
<!-- Windows, blue detail -->
<g fill="#4488ee">
<rect x="5" y="100" width="8" height="10" rx="2"/><rect x="18" y="100" width="8" height="10" rx="2"/>
<rect x="33" y="88" width="8" height="10" rx="2"/><rect x="83" y="84" width="8" height="10" rx="2"/>
</g>
<!-- Sea -->
<rect x="0" y="140" width="150" height="10" fill="#1144aa" opacity=".6"/>
</svg>
</div>
<div class="ig-04__meta">Santorini, 2005</div>
</div>
<!-- Polaroid 4: Safari elephant -->
<div class="ig-04__card">
<div class="ig-04__photo">
<svg viewBox="0 0 150 150" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="sfg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#e8c870"/><stop offset="50%" stop-color="#d4a840"/><stop offset="100%" stop-color="#8a5010"/></linearGradient></defs>
<rect width="150" height="150" fill="url(#sfg)"/>
<!-- Acacia tree -->
<rect x="110" y="80" width="6" height="70" fill="#4a2808"/>
<ellipse cx="113" cy="78" rx="28" ry="12" fill="#2a5a14"/>
<ellipse cx="100" cy="72" rx="20" ry="9" fill="#3a7020"/>
<ellipse cx="126" cy="70" rx="18" ry="8" fill="#2a5a14"/>
<!-- Elephant -->
<g transform="translate(20,80)" fill="#7a7060">
<ellipse cx="40" cy="30" rx="38" ry="26"/> <!-- body -->
<ellipse cx="14" cy="22" rx="18" ry="20"/> <!-- head -->
<!-- Ear -->
<ellipse cx="2" cy="18" rx="12" ry="15" fill="#8a8070"/>
<!-- Trunk -->
<path d="M5,32 Q-5,42 -2,58 Q0,65 5,60 Q8,52 5,42 Q12,38 10,32" fill="#7a7060"/>
<!-- Legs -->
<rect x="10" y="50" width="12" height="30" rx="5" fill="#6a6050"/>
<rect x="26" y="50" width="12" height="32" rx="5" fill="#6a6050"/>
<rect x="44" y="50" width="12" height="30" rx="5" fill="#6a6050"/>
<rect x="60" y="50" width="12" height="28" rx="5" fill="#6a6050"/>
<!-- Tusk -->
<path d="M8,36 Q-2,44 -5,52" stroke="#f0e8c0" stroke-width="3" fill="none" stroke-linecap="round"/>
<!-- Eye -->
<circle cx="8" cy="16" r="3" fill="#1a1a14"/>
<circle cx="9" cy="15" r="1" fill="#fff"/>
<!-- Tail -->
<path d="M78,32 Q88,35 90,44" stroke="#6a6050" stroke-width="2.5" fill="none"/>
</g>
<!-- Baby elephant -->
<g transform="translate(72,100)" fill="#8a8070" opacity=".85">
<ellipse cx="18" cy="16" rx="18" ry="13"/>
<ellipse cx="5" cy="11" rx="9" ry="10"/>
<path d="M2,15 Q-4,22 -2,30 Q0,34 3,30 Q5,24 2,18" fill="#8a8070"/>
<rect x="4" y="25" width="6" height="14" rx="3" fill="#7a7060"/>
<rect x="13" y="25" width="6" height="14" rx="3" fill="#7a7060"/>
<rect x="22" y="25" width="6" height="14" rx="3" fill="#7a7060"/>
<rect x="30" y="25" width="6" height="12" rx="3" fill="#7a7060"/>
</g>
<!-- Grass tufts -->
<g stroke="#6a8a28" stroke-width="1.5" stroke-linecap="round" fill="none" opacity=".7">
<line x1="5" y1="148" x2="3" y2="135"/><line x1="10" y1="148" x2="12" y2="136"/>
<line x1="145" y1="148" x2="143" y2="134"/><line x1="138" y1="148" x2="140" y2="136"/>
</g>
</svg>
</div>
<div class="ig-04__meta">Amboseli, 2010</div>
</div>
<!-- Polaroid 5: Northern Lights cabin -->
<div class="ig-04__card">
<div class="ig-04__photo">
<svg viewBox="0 0 150 150" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="nlg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#040820"/><stop offset="50%" stop-color="#0a1535"/><stop offset="100%" stop-color="#0d1e25"/></linearGradient><filter id="nlf"><feGaussianBlur stdDeviation="4"/></filter></defs>
<rect width="150" height="150" fill="url(#nlg)"/>
<g opacity=".7" filter="url(#nlf)">
<path d="M0,45 Q38,22 75,45 Q112,62 150,32" stroke="#00e8a0" stroke-width="18" fill="none"/>
<path d="M0,62 Q40,38 80,56 Q118,75 150,48" stroke="#6040ff" stroke-width="12" fill="none"/>
<path d="M0,35 Q30,18 60,38 Q95,58 150,26" stroke="#00c8e0" stroke-width="8" fill="none"/>
</g>
<g fill="#fff" opacity=".7"><circle cx="10" cy="8" r=".8"/><circle cx="30" cy="4" r="1"/><circle cx="60" cy="10" r=".9"/><circle cx="90" cy="5" r="1.1"/><circle cx="120" cy="8" r=".8"/><circle cx="145" cy="4" r="1"/></g>
<!-- Snow ground -->
<rect x="0" y="115" width="150" height="35" fill="#c8d8e8"/>
<path d="M0,112 Q38,104 75,110 Q112,104 150,108 L150,115 L0,115 Z" fill="#d8e8f5"/>
<!-- Cabin -->
<g fill="#2a1808"><rect x="50" y="95" width="50" height="28"/><polygon points="44,95 75,78 106,95"/></g>
<rect x="55" y="100" width="12" height="12" fill="#ff9040" opacity=".9"/>
<rect x="83" y="100" width="12" height="12" fill="#ff9040" opacity=".9"/>
<rect x="67" y="105" width="10" height="18" fill="#5a2808"/>
<!-- Window glow -->
<rect x="55" y="100" width="12" height="12" fill="#ffaa60" opacity=".4" filter="url(#nlf)"/>
<rect x="83" y="100" width="12" height="12" fill="#ffaa60" opacity=".4" filter="url(#nlf)"/>
<!-- Pine trees -->
<g fill="#0d1e10">
<polygon points="15,115 22,88 29,115"/><polygon points="18,100 22,88 26,100"/>
<polygon points="125,115 132,92 139,115"/><polygon points="128,102 132,92 136,102"/>
</g>
<!-- Chimney smoke -->
<path d="M76,78 Q74,68 78,58 Q80,48 76,40" stroke="#888" stroke-width="3" fill="none" opacity=".5" stroke-linecap="round"/>
</svg>
</div>
<div class="ig-04__meta">Lapland, 2015</div>
</div>
</div>
</div>
<p class="ig-04__hint">Hover to fan · Click to flip</p>
</div>
<script>
(function(){
const stack=document.getElementById('ig-04-stack');
if(!stack)return;
const cards=Array.from(stack.querySelectorAll('.ig-04__card'));
const rots=cards.map(()=>(Math.random()-.5)*16);
cards.forEach((c,i)=>{c.style.transform=`rotate(${rots[i]}deg)`;c.style.zIndex=i;});
stack.addEventListener('mouseenter',()=>{
const spread=20,total=cards.length;
cards.forEach((c,i)=>{
const angle=-spread/2+(spread/(total-1))*i;
const tx=-130+(260/(total-1))*i;
c.style.transform=`translateX(${tx}px) rotate(${angle}deg)`;
c.style.setProperty('--ft',`translateX(${tx}px) rotate(${angle}deg)`);
});
});
stack.addEventListener('mouseleave',()=>{
cards.forEach((c,i)=>{c.style.transform=`rotate(${rots[i]}deg)`;c.classList.remove('ig-04--active');});
});
cards.forEach((c,i)=>{
c.addEventListener('click',()=>{
cards.forEach((x,j)=>{x.classList.remove('ig-04--active');x.style.zIndex=j;});
c.classList.add('ig-04--active');c.style.zIndex=cards.length+1;
c.addEventListener('animationend',()=>c.classList.remove('ig-04--active'),{once:true});
});
});
}());
</script>*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
body{background:radial-gradient(ellipse at center,#3a2a1a 0%,#1a1008 100%);font-family:'DM Sans',sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem}
.ig-04{--card-w:170px;display:flex;flex-direction:column;align-items:center;gap:1.8rem}
.ig-04__scene{perspective:900px;height:260px;width:100%;display:flex;align-items:center;justify-content:center}
.ig-04__stack{position:relative;width:var(--card-w);height:220px;cursor:pointer}
.ig-04__card{position:absolute;inset:0;background:#faf6ee;border-radius:3px;padding:10px 10px 44px;box-shadow:0 8px 30px rgba(0,0,0,.55),0 2px 6px rgba(0,0,0,.3);transition:transform .5s cubic-bezier(.34,1.56,.64,1),box-shadow .4s ease;will-change:transform;transform-origin:center bottom}
.ig-04__photo{width:100%;aspect-ratio:1;display:block}
.ig-04__photo svg{width:100%;height:100%;display:block}
.ig-04__meta{text-align:center;font-family:'Caveat',cursive;font-size:1rem;color:#4a3a28;margin-top:.4rem;letter-spacing:.02em}
.ig-04__card.ig-04--active{animation:ig-04-flip .65s cubic-bezier(.34,1.56,.64,1);box-shadow:0 28px 70px rgba(0,0,0,.65)}
@keyframes ig-04-flip{0%{transform:var(--ft,rotate(0deg)) scale(1)}50%{transform:var(--ft,rotate(0deg)) scale(1.18) rotateY(180deg)}100%{transform:var(--ft,rotate(0deg)) scale(1.1) rotateY(360deg)}}
.ig-04__hint{color:rgba(255,255,255,.3);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase}
@media(prefers-reduced-motion:reduce){.ig-04__card{transition:none}.ig-04__card.ig-04--active{animation:none}} *,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
body{background:radial-gradient(ellipse at center,#3a2a1a 0%,#1a1008 100%);font-family:'DM Sans',sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;padding:2rem}
.ig-04{--card-w:170px;display:flex;flex-direction:column;align-items:center;gap:1.8rem}
.ig-04__scene{perspective:900px;height:260px;width:100%;display:flex;align-items:center;justify-content:center}
.ig-04__stack{position:relative;width:var(--card-w);height:220px;cursor:pointer}
.ig-04__card{position:absolute;inset:0;background:#faf6ee;border-radius:3px;padding:10px 10px 44px;box-shadow:0 8px 30px rgba(0,0,0,.55),0 2px 6px rgba(0,0,0,.3);transition:transform .5s cubic-bezier(.34,1.56,.64,1),box-shadow .4s ease;will-change:transform;transform-origin:center bottom}
.ig-04__photo{width:100%;aspect-ratio:1;display:block}
.ig-04__photo svg{width:100%;height:100%;display:block}
.ig-04__meta{text-align:center;font-family:'Caveat',cursive;font-size:1rem;color:#4a3a28;margin-top:.4rem;letter-spacing:.02em}
.ig-04__card.ig-04--active{animation:ig-04-flip .65s cubic-bezier(.34,1.56,.64,1);box-shadow:0 28px 70px rgba(0,0,0,.65)}
@keyframes ig-04-flip{0%{transform:var(--ft,rotate(0deg)) scale(1)}50%{transform:var(--ft,rotate(0deg)) scale(1.18) rotateY(180deg)}100%{transform:var(--ft,rotate(0deg)) scale(1.1) rotateY(360deg)}}
.ig-04__hint{color:rgba(255,255,255,.3);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase}
@media(prefers-reduced-motion:reduce){.ig-04__card{transition:none}.ig-04__card.ig-04--active{animation:none}}(function(){
const stack=document.getElementById('ig-04-stack');
if(!stack)return;
const cards=Array.from(stack.querySelectorAll('.ig-04__card'));
const rots=cards.map(()=>(Math.random()-.5)*16);
cards.forEach((c,i)=>{c.style.transform=`rotate(${rots[i]}deg)`;c.style.zIndex=i;});
stack.addEventListener('mouseenter',()=>{
const spread=20,total=cards.length;
cards.forEach((c,i)=>{
const angle=-spread/2+(spread/(total-1))*i;
const tx=-130+(260/(total-1))*i;
c.style.transform=`translateX(${tx}px) rotate(${angle}deg)`;
c.style.setProperty('--ft',`translateX(${tx}px) rotate(${angle}deg)`);
});
});
stack.addEventListener('mouseleave',()=>{
cards.forEach((c,i)=>{c.style.transform=`rotate(${rots[i]}deg)`;c.classList.remove('ig-04--active');});
});
cards.forEach((c,i)=>{
c.addEventListener('click',()=>{
cards.forEach((x,j)=>{x.classList.remove('ig-04--active');x.style.zIndex=j;});
c.classList.add('ig-04--active');c.style.zIndex=cards.length+1;
c.addEventListener('animationend',()=>c.classList.remove('ig-04--active'),{once:true});
});
});
}()); (function(){
const stack=document.getElementById('ig-04-stack');
if(!stack)return;
const cards=Array.from(stack.querySelectorAll('.ig-04__card'));
const rots=cards.map(()=>(Math.random()-.5)*16);
cards.forEach((c,i)=>{c.style.transform=`rotate(${rots[i]}deg)`;c.style.zIndex=i;});
stack.addEventListener('mouseenter',()=>{
const spread=20,total=cards.length;
cards.forEach((c,i)=>{
const angle=-spread/2+(spread/(total-1))*i;
const tx=-130+(260/(total-1))*i;
c.style.transform=`translateX(${tx}px) rotate(${angle}deg)`;
c.style.setProperty('--ft',`translateX(${tx}px) rotate(${angle}deg)`);
});
});
stack.addEventListener('mouseleave',()=>{
cards.forEach((c,i)=>{c.style.transform=`rotate(${rots[i]}deg)`;c.classList.remove('ig-04--active');});
});
cards.forEach((c,i)=>{
c.addEventListener('click',()=>{
cards.forEach((x,j)=>{x.classList.remove('ig-04--active');x.style.zIndex=j;});
c.classList.add('ig-04--active');c.style.zIndex=cards.length+1;
c.addEventListener('animationend',()=>c.classList.remove('ig-04--active'),{once:true});
});
});
}());How this works
Each polaroid card has a random rotation applied via JavaScript on load using Math.random() to set an inline transform: rotate(Ndeg). On stack hover, JS fans the cards with both translateX and rotate values spread evenly across the count — creating a arc effect. A CSS transition: transform .5s cubic-bezier(.34,1.56,.64,1) provides the spring bounce.
The click flip uses a CSS @keyframes ig-04-flip animation that rotates the card 360 degrees in rotateY while briefly scaling up, applied by toggling the .ig-04--active class. The --ft CSS custom property stores the fanned position so the flip starts and ends at the correct angle.
Customize
- Widen the fan spread by increasing the
spreadvariable in the JS from20to30degrees, and thetranslateXrange from260to360px. - Change the polaroid padding-bottom to adjust the caption space —
padding: 10px 10px 44pxcontrols the white border and lower white strip. - Make all cards the same rotation on load by removing the
Math.random()and setting a fixed stagger likei * 5 - 10degrees. - Add a tape strip above each card by inserting a small
::beforepseudo-element withbackground: rgba(255,240,200,.55). - Change font to a different handwritten feel by swapping
CaveatforPacificoorSatisfyin the Google Fonts import.
Watch out for
- The
--ftCSS custom property set inline via JS can be overwritten by later JS transforms; always set it before applying the active class. animation-fill-mode: forwardsis required on the flip keyframe, otherwise the card snaps back to its pre-animation state when the animation ends.- CSS perspective on the parent must be set for
rotateYto look three-dimensional — withoutperspective: 900pxon a parent, the flip appears flat.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 36+ | 9+ | 16+ | 36+ |
CSS transforms and 3D perspective broadly supported; CSS custom properties require Chrome 49+.