Simple Image Gallery with display: grid; and object-fit: cover;

On the Full Stack Belgium Slack channel, user @Brammm recently asked how to create a simple image gallery.

Anyone have a favorite way of making an image grid with CSS? I’d like one of those “fancy” ones where no matter the aspect ratio of the image, they all have the same gap between them.

While some suggested existing solutions such as React Photo Gallery or photo-stream, I focussed on only the layout-question and suggested a DIY solution using display: grid; and object-fit: cover;.

Using only those those two props it’s really easy to create a grid based gallery.

  • Using display: grid; you create a grid layout. I decided to create a grid of square cells/tiles.
  • As I stretch out each image inside a tile, the images can get deformed. Using object-fit: cover; this can be prevented. Note that you will visually lose some data, as the edges will get clipped, but that was not a concern to @Brammm.


Proof Of Concept

Using photos from the birth of my son Noah I knocked up a small small proof of concept in about 10 minutes. Tweaking it a bit more – by adding some CSS Variables into the mix – I eventually landed on this:

See the Pen
Simple Gallery (display: grid; + object-fit: cover;) V1
by Bramus (@bramus)
on CodePen.

💁‍♂️ To create a consistent gap between all items I used the CSS gap property, which replaces grid-gap.


Making it feel more dynamic

To make things visually more interesting, and also since some photos are portrait and some landscape, the size of the tiles must differ a bit. With CSS Grid in place it’s really easy to stretch out cells so that they span more than one column or row: > li.wide {
    grid-column: span 2;
} > li.wider {
    grid-column: span 3;
} > li.high {
    grid-row: span 2;
    height: auto; /* to undo the height */

With these in place, my gallery started to look like this:

There, looks good, right? 🙂

😋 I know, I cheated a bit as I added the .wide/.high classes onto carefully selected tiles, leaving no gaps in my grid. To workaround potential gaps, one can always use grid-auto-flow: dense;, or use the (still under development) Grid Masonry from the Grid Level 2 spec. Note that in those cases the order of the images will differ from their source order.


Going further: Zoom / Lightbox functionality

Where I had originally stopped working on the gallery after that, today I wondered if I could adjust it a bit further and give it the ability to show the images at full screen, in some sort of Lightbox. Still using only CSS it’s possible to show an overlay while pressing+holding a tile.

  • Using the :active pseudo-selector you can know which element is currently being pressed
  • Using position: fixed; you can put content on top of the entire viewport

Combining the two as follows … > li:active > img {
    position: fixed; /* Position the image on top of the viewport contents */
    z-index: 11;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    object-fit: contain; /* Make sure the image isn't distorted */
    padding: 1vw; /* Add some padding to make it breathe a bit */
    background: rgba(0, 0, 0, 0.8); /* Dim the background */

… will get you this:

See the Pen
Simple Gallery (display: grid; + object-fit: cover;) V3 (WIP)
by Bramus (@bramus)
on CodePen.

While the version above does work (on Desktop), there’s a huge problem with it: it’s not (keyboard) accessible at all. In order to give the browser – and therefore user – hints about stuff being clickable we could add a truckload of ARIA attributes or simply use … links. An extra benefit of this approach is that we then start using CSS :target, and eventually create Pure CSS Lightbox.

See the Pen
Simple Gallery (display: grid; + object-fit: cover;) V4b
by Bramus (@bramus)
on CodePen.

It’s not entirely perfect though, when using only the keyboard to navigate, you have to do some trickery to close the Lightbox: after zooming in on an image you’ll have to hit TAB to focus the close link and then hit ENTER to activate it — If only autofocus where available on non-inputs …

As an extra I also added some JS to make the , , and ESC keys work, so that it behaves like a “real” Lightbox. There’s plenty more things I could add to it, but that’s something way beyond the original goal of this post so I left it at that.


Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Gamma Gallery: A Responsive Image Gallery

Gamma Gallery is an experimental responsive image gallery that attempts to provide an adjustable responsive images approach taking its grid layout and the full slideshow view into account.

Both the grid and lightbox are responsive.

Gamma Gallery: A Responsive Image Gallery Experiment →
Gamma Gallery Demo →

My Code Snippet : Automatically start Lightbox on page load (autoFireLightbox)

lightbox.gifEver wanted to automatically start Lightbox so that it shows an image immediately after the page has loaded? It’s possible!

Someone posted a little Javascript Code Snippet that provides this functionality in the Lightbox Forums. Although the code is OK, it requires editing Lightbox.js, which will of course break when a new version is released (it’s never a clever idea to edit core files).

I took the freedom to modify the implementation a bit (the core hasn’t changed though): Instead of editing your Lightbox.js file, you only need to modify your HTML head element so that it includes the extra functionality of kickstarting Lightbox:

<script src="js/prototype.js" type="text/javascript"></script>
<script src="js/scriptaculous.js?load=effects" type="text/javascript"></script>
<script src="js/lightbox.js" type="text/javascript"></script>

<script type="text/javascript">
// Automagically load Lightbox on Page Load - by Bramus! (
// Code modded from
function autoFireLightbox() {
	//Check if location.hash matches a lightbox-anchor. If so, trigger popup of image.
	setTimeout(function() {
		if(document.location.hash && $(document.location.hash.substr(1)).rel.indexOf('lightbox')!=-1) {
Event.observe(window, 'load', autoFireLightbox, false);

Note #1: it is important that this extra snippet is placed underneath the 3 javascript includes required by Lightbox as the function calls a variable created within Lightbox.js

Note #2: the reason we’re calling a setTimeout (a 250 milliseconds delay) is to fix an issue in IE where the variable myLightbox would be non-existent immediately after page load.

Note #3: getting a myLightBox is undefined error? Great, you’re using the most recent Lightbox version … and that one requires 1 tiny fix 😉

To now use this functionality make sure you have given your a-element an id …

<a href="images/image-1.jpg" rel="lightbox" id="image001"><img src="images/thumb-1.jpg" width="100" height="40" alt="" /></a>

… and then call the page by adding a # followed by that id:

That’s it, you’re done!

A working demo can be found over at

To make things complete: this will not work with flashLightBoxInjector as the swf (or a return from your AJAX call, that’s possible too you know), dynamically builds a list of linked images when the swf is loaded/call has been returned.

Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)