~/css/tailwind-image-galleries
6 Tailwind Image Galleries
3 Tailwind 3 with JS Published 2026-10-05 Free · no sign-up
Six galleries built with Tailwind: thumbnails that open in a lightbox dialog, a product gallery with thumbnails, masonry columns, captions on hover, category filters and a pile of polaroid-style photos.
Filter demos
01. Tailwind Gallery with Lightbox
Tailwind Gallery Dialog
A thumbnail grid where each picture opens larger in a modal dialog with its caption. Thumbnails are buttons with descriptive names, and the dialog closes with Escape, the Close button or a click on the dimmed backdrop.
Copy HTML Copy JS Open in editor →
View code
HTML JS
<ul id="twig1" class="grid w-full max-w-xs grid-cols-3 gap-2">
<li><button type="button" data-cap="Harbour at dusk" aria-label="Open photo: Harbour at dusk" style="background:linear-gradient(#fdba74,#f472b6 45%,#1e3a8a 46%,#0f172a)" class="block aspect-square w-full rounded-lg focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"></button></li>
<li><button type="button" data-cap="Pine forest" aria-label="Open photo: Pine forest" style="background:linear-gradient(#bbf7d0,#15803d 60%,#14532d)" class="block aspect-square w-full rounded-lg focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"></button></li>
<li><button type="button" data-cap="Desert dunes" aria-label="Open photo: Desert dunes" style="background:linear-gradient(#fef3c7,#f59e0b 55%,#b45309)" class="block aspect-square w-full rounded-lg focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"></button></li>
</ul>
<dialog id="twig1d" aria-labelledby="twig1c" class="w-[min(90vw,420px)] rounded-2xl bg-white p-3 backdrop:bg-black/70">
<div id="twig1i" role="img" class="aspect-[4/3] rounded-xl"></div>
<div class="mt-2 flex items-center justify-between"><p id="twig1c" class="text-sm font-semibold text-slate-900"></p><button type="button" data-close class="rounded-lg bg-slate-900 px-3 py-1.5 text-sm font-semibold text-white hover:bg-slate-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-900">Close</button></div>
</dialog>var d = document.getElementById('twig1d'), im = document.getElementById('twig1i');
document.querySelectorAll('#twig1 button').forEach(function (b) { b.onclick = function () { im.style.background = b.style.background; im.setAttribute('aria-label', b.dataset.cap); document.getElementById('twig1c').textContent = b.dataset.cap; d.showModal(); }; });
d.querySelector('[data-close]').onclick = function () { d.close(); };
d.addEventListener('click', function (e) { if (e.target === d) d.close(); });
02. Tailwind Product Gallery with Thumbnails
Tailwind Gallery Product
A main product picture with a row of thumbnails underneath, as on a shop's product page. The selected thumbnail carries aria-current="true" and a ring, and the main picture's label changes with it.
Copy HTML Copy JS Open in editor →
View code
HTML JS
<div class="w-full max-w-[260px]">
<div id="twig2m" role="img" aria-label="Ceramic mug, front" class="aspect-square rounded-2xl bg-[radial-gradient(circle_at_50%_55%,#0f766e_0_28%,transparent_29%),#ccfbf1]"></div>
<div id="twig2t" class="mt-2 grid grid-cols-3 gap-2">
<button type="button" aria-current="true" aria-label="Show front" data-label="Ceramic mug, front" data-bg="radial-gradient(circle at 50% 55%,#0f766e 0 28%,transparent 29%),#ccfbf1" class="aspect-square rounded-lg bg-teal-100 ring-offset-2 aria-[current=true]:ring-2 aria-[current=true]:ring-teal-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-teal-700"><span aria-hidden="true" class="mx-auto block size-6 rounded-full bg-teal-700"></span></button>
<button type="button" aria-label="Show side" data-label="Ceramic mug, side with handle" data-bg="radial-gradient(ellipse at 45% 55%,#0f766e 0 24%,transparent 25%),radial-gradient(circle at 72% 55%,transparent 0 6%,#0f766e 7% 11%,transparent 12%),#ccfbf1" class="aspect-square rounded-lg bg-teal-100 ring-offset-2 aria-[current=true]:ring-2 aria-[current=true]:ring-teal-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-teal-700"><span aria-hidden="true" class="mx-auto block h-6 w-5 rounded-md bg-teal-700"></span></button>
<button type="button" aria-label="Show top" data-label="Ceramic mug, top view" data-bg="radial-gradient(circle at 50% 50%,#134e4a 0 18%,#0f766e 19% 28%,transparent 29%),#ccfbf1" class="aspect-square rounded-lg bg-teal-100 ring-offset-2 aria-[current=true]:ring-2 aria-[current=true]:ring-teal-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-teal-700"><span aria-hidden="true" class="mx-auto block size-6 rounded-full border-4 border-teal-700 bg-teal-900"></span></button>
</div>
</div>var m = document.getElementById('twig2m'), bs = document.querySelectorAll('#twig2t button');
bs.forEach(function (b) { b.onclick = function () { bs.forEach(function (x) { x.removeAttribute('aria-current'); }); b.setAttribute('aria-current', 'true'); m.style.background = b.dataset.bg; m.setAttribute('aria-label', b.dataset.label); }; });
03. Tailwind Masonry Photo Gallery
Tailwind Gallery Masonry
Pictures of different shapes flow into two columns with columns-2, each figure kept whole by break-inside-avoid. Aspect-ratio utilities set the shapes, and each caption sits under its picture.
Copy HTML Open in editor →
View code
HTML
<div class="w-full max-w-xs columns-2 gap-2">
<figure class="mb-2 break-inside-avoid"><div role="img" aria-label="Tall waterfall" class="aspect-[3/5] rounded-lg bg-[linear-gradient(#e0f2fe,#38bdf8_40%,#0c4a6e)]"></div><figcaption class="mt-1 text-xs text-slate-300">Waterfall</figcaption></figure>
<figure class="mb-2 break-inside-avoid"><div role="img" aria-label="Wide meadow" class="aspect-[4/3] rounded-lg bg-[linear-gradient(#fef9c3,#a3e635_50%,#3f6212)]"></div><figcaption class="mt-1 text-xs text-slate-300">Meadow</figcaption></figure>
<figure class="mb-2 break-inside-avoid"><div role="img" aria-label="Square city rooftop" class="aspect-square rounded-lg bg-[linear-gradient(#fda4af,#7c3aed_60%,#1e1b4b)]"></div><figcaption class="mt-1 text-xs text-slate-300">Rooftops</figcaption></figure>
<figure class="mb-2 break-inside-avoid"><div role="img" aria-label="Tall lighthouse" class="aspect-[3/4] rounded-lg bg-[linear-gradient(#fde68a,#f97316_50%,#7c2d12)]"></div><figcaption class="mt-1 text-xs text-slate-300">Lighthouse</figcaption></figure>
</div>
04. Tailwind Gallery with Hover Captions
Tailwind Gallery Caption
Captions fade in over the bottom of each picture on hover, with a gradient behind them for contrast. On touch screens the captions are always visible, and screen readers read them at all times.
Copy HTML Open in editor →
View code
HTML
<ul class="grid w-full max-w-xs grid-cols-2 gap-2">
<li><figure class="group relative overflow-hidden rounded-xl"><div role="img" aria-label="Boats in a harbour" class="aspect-square bg-[linear-gradient(#bae6fd,#0284c7_55%,#0c4a6e)]"></div><figcaption class="absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/80 to-transparent p-2 pt-6 text-xs font-semibold text-white opacity-0 transition-opacity group-hover:opacity-100 [@media(hover:none)]:opacity-100">Harbour, Porto</figcaption></figure></li>
<li><figure class="group relative overflow-hidden rounded-xl"><div role="img" aria-label="Orange grove" class="aspect-square bg-[radial-gradient(circle_at_40%_40%,#fb923c_0_14%,transparent_15%),linear-gradient(#bbf7d0,#166534)]"></div><figcaption class="absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/80 to-transparent p-2 pt-6 text-xs font-semibold text-white opacity-0 transition-opacity group-hover:opacity-100 [@media(hover:none)]:opacity-100">Orange grove, Seville</figcaption></figure></li>
</ul>
05. Tailwind Filterable Gallery
Tailwind Gallery Filter
Category buttons above the gallery show only matching pictures. The buttons are toggle buttons with aria-pressed, the hidden pictures use the hidden attribute, and a status line reports the count.
Copy HTML Copy JS Open in editor →
View code
HTML JS
<div class="w-full max-w-xs">
<div id="twig5f" role="group" aria-label="Filter photos" class="flex gap-2 text-sm font-semibold">
<button type="button" data-f="all" aria-pressed="true" class="rounded-full px-3 py-1 text-slate-700 ring-1 ring-slate-300 aria-pressed:bg-slate-900 aria-pressed:text-white aria-pressed:ring-slate-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">All</button>
<button type="button" data-f="coast" aria-pressed="false" class="rounded-full px-3 py-1 text-slate-700 ring-1 ring-slate-300 aria-pressed:bg-slate-900 aria-pressed:text-white aria-pressed:ring-slate-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Coast</button>
<button type="button" data-f="city" aria-pressed="false" class="rounded-full px-3 py-1 text-slate-700 ring-1 ring-slate-300 aria-pressed:bg-slate-900 aria-pressed:text-white aria-pressed:ring-slate-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">City</button>
</div>
<p id="twig5s" role="status" class="mt-2 text-xs text-slate-600">4 photos</p>
<ul id="twig5" class="mt-1 grid grid-cols-2 gap-2">
<li data-c="coast"><div role="img" aria-label="Cliffs over the sea" class="aspect-[4/3] rounded-lg bg-[linear-gradient(#bae6fd,#0369a1)]"></div></li>
<li data-c="city"><div role="img" aria-label="Night skyline" class="aspect-[4/3] rounded-lg bg-[linear-gradient(#a78bfa,#1e1b4b)]"></div></li>
<li data-c="coast"><div role="img" aria-label="Sandy cove" class="aspect-[4/3] rounded-lg bg-[linear-gradient(#fde68a,#0ea5e9)]"></div></li>
<li data-c="city"><div role="img" aria-label="Tram street" class="aspect-[4/3] rounded-lg bg-[linear-gradient(#fdba74,#9a3412)]"></div></li>
</ul>
</div>var bs = document.querySelectorAll('#twig5f button'), items = document.querySelectorAll('#twig5 li');
bs.forEach(function (b) { b.onclick = function () { var f = b.dataset.f, n = 0; bs.forEach(function (x) { x.setAttribute('aria-pressed', x === b); }); items.forEach(function (li) { var on = f === 'all' || li.dataset.c === f; li.hidden = !on; if (on) n++; }); document.getElementById('twig5s').textContent = n + ' photos'; }; });
06. Tailwind Polaroid Photo Pile
Tailwind Gallery Polaroid
Instant-photo frames scattered at slight angles, each straightening and lifting on hover. A white frame with a deeper bottom edge and a handwritten-style caption gives the snapshot look.
Copy HTML Open in editor →
View code
HTML
<ul class="flex w-full max-w-xs justify-center -space-x-6 py-4">
<li><figure class="w-28 -rotate-6 bg-white p-2 pb-6 shadow-lg transition hover:relative hover:z-10 hover:rotate-0 hover:scale-110 motion-reduce:transition-none"><div role="img" aria-label="Beach at noon" class="aspect-square bg-[linear-gradient(#bae6fd_55%,#fde68a_55%)]"></div><figcaption class="mt-1 text-center font-serif text-xs italic text-slate-700">Beach day</figcaption></figure></li>
<li><figure class="w-28 rotate-3 bg-white p-2 pb-6 shadow-lg transition hover:relative hover:z-10 hover:rotate-0 hover:scale-110 motion-reduce:transition-none"><div role="img" aria-label="Birthday cake with candles" class="aspect-square bg-[linear-gradient(#fbcfe8_50%,#f472b6_50%)]"></div><figcaption class="mt-1 text-center font-serif text-xs italic text-slate-700">Turning 30</figcaption></figure></li>
<li><figure class="w-28 -rotate-2 bg-white p-2 pb-6 shadow-lg transition hover:relative hover:z-10 hover:rotate-0 hover:scale-110 motion-reduce:transition-none"><div role="img" aria-label="Snowy mountain" class="aspect-square bg-[linear-gradient(160deg,#f8fafc_40%,#94a3b8_41%,#334155)]"></div><figcaption class="mt-1 text-center font-serif text-xs italic text-slate-700">Alps</figcaption></figure></li>
</ul>
About these Tailwind Image Galleries
Thumbnails are buttons
A thumbnail that opens something larger is a button with a name such as Open photo: Harbour at dusk.
Lightbox on dialog
showModal() traps focus and closes on Escape; backdrop:bg-black/70 dims the page.
Masonry with columns
columns-2 and break-inside-avoid make a masonry gallery; reading order runs down each column.
Captions for everyone
Hover captions should still be readable by screen readers and always visible on touch screens through [@media(hover:none)]:opacity-100.
Frequently asked questions
Can I use real images? Yes. Replace the gradient placeholders with img elements and alt text, keeping the classes.
Should images be lazy loaded? Yes, add loading="lazy" to images below the fold.
Is there a CSS version? Yes, see the CSS image galleries collection.