Photo Grid Gallery
An even, responsive grid of captioned photos with no JavaScript.
6 frameworksBeginner
A responsive grid whose tiles scale up on hover and keyboard focus.
<!--
The zoom is on the tile, the clip is on the button: group-hover AND
group-focus-visible both drive it, so a keyboard user tabbing through gets the
same feedback a mouse does. motion-reduce cancels the scale for anyone who
asked the OS to still animations.
-->
<ul class="grid w-full list-none grid-cols-2 gap-3 p-0 sm:grid-cols-3 lg:grid-cols-4">
<li>
<button type="button" class="group block w-full overflow-hidden rounded-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 dark:focus-visible:ring-blue-400 dark:focus-visible:ring-offset-gray-950">
<span class="block aspect-square w-full bg-gradient-to-br from-sky-400 to-indigo-600 transition-transform duration-300 group-hover:scale-110 group-focus-visible:scale-110 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus-visible:scale-100" role="img" aria-label="Sky-blue to indigo gradient"></span>
</button>
</li>
<li>
<button type="button" class="group block w-full overflow-hidden rounded-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 dark:focus-visible:ring-blue-400 dark:focus-visible:ring-offset-gray-950">
<span class="block aspect-square w-full bg-gradient-to-br from-rose-400 to-orange-500 transition-transform duration-300 group-hover:scale-110 group-focus-visible:scale-110 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus-visible:scale-100" role="img" aria-label="Rose to orange gradient"></span>
</button>
</li>
<li>
<button type="button" class="group block w-full overflow-hidden rounded-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 dark:focus-visible:ring-blue-400 dark:focus-visible:ring-offset-gray-950">
<span class="block aspect-square w-full bg-gradient-to-br from-emerald-400 to-teal-600 transition-transform duration-300 group-hover:scale-110 group-focus-visible:scale-110 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus-visible:scale-100" role="img" aria-label="Emerald to teal gradient"></span>
</button>
</li>
<li>
<button type="button" class="group block w-full overflow-hidden rounded-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 dark:focus-visible:ring-blue-400 dark:focus-visible:ring-offset-gray-950">
<span class="block aspect-square w-full bg-gradient-to-br from-violet-400 to-fuchsia-600 transition-transform duration-300 group-hover:scale-110 group-focus-visible:scale-110 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus-visible:scale-100" role="img" aria-label="Violet to fuchsia gradient"></span>
</button>
</li>
</ul>| Prop | Type | Default | Description |
|---|---|---|---|
itemsrequired | GalleryTile[] | - | The array of items to render. |
onSelect | (index: number) => void | - | Fired with the menu item the user chose. |
className | string | - | Additional classes merged onto the root element. |
The scale sits on the inner tile and the clip on the button, so `group-hover` and `group-focus-visible` produce identical feedback - a keyboard user gets the zoom a mouse user does. `motion-reduce` cancels the scale for anyone who asked the OS to still animations.