A very flexible full-width image grid using flex
The current solution
- Grab the first image and get it’s width and height and calculate it’s aspect ratio.
- Based on that ratio calculate the width of the next image keeping the images height equal.
- Compare the total width of the images with the width of the parent. If the width of the images is less than the parent repeat step 2.
- Decrease each image width equally until the width of all images equals the width of the parent. Make sure to also decrease the height of each image based on the aspect ratio from step 1.
This is a somewhat complex loop and requires us to keep track of all images and their aspect ratio and then recalculate it all whenever the parents width changes.
Why not try to be smart about it and remove that need?
The ideal solution?
Each row needs to have the same height while at the same time make sure it fills the entire width of the parent. This requires us to keep track of each image width/height ratio.
Some background on how Flex works
But instead of calculating each image width ourselves we could let CSS take care of that. If we just tell our CSS how to do it it turns out this is actually possible.
There’s a new display mode that’s getting more and more popular and except for IE it’s actually well supported. With a combination of
flex-basis we can give our CSS all it needs to keep track of our images on it’s own. The way flex-shrink works is that if our images won’t fit on a single row it will use this factor to start shrink the images until it can fit them on one row. But we don’t want just that, we also want them to flow to the next row, this is solved by using
When using flex-wrap in combination with flex-shrink our elements will only be shrunk as much that they just fit on a row, this is based of each elements flex-basis. Meaning that if we can fit 3 images on our row we’ll get 4, shrunk just so much that they all fit.
How to solve the problem
With the above knowledge we realise that the only think we need is to keep track of each image aspect ratio for it all to come together on it’s own.
If we just set each image
flex-basis based on its aspect ratio our CSS will take care of making sure each row fills up with just the right number of images. If we use
flex-shrink right it will resize each image based on it’s aspect ratio and as a result give you a row of images with the same height.
So what will this end up like? The result is a set of .grid__element items with
flex-basis: 414.575px and
flex-grow: 2.07287 set.
<div id="grid" class="grid"> <div class="grid__element" style="flex-basis: 414.575px; flex-grow: 2.07287;"> <img src="https://farm2.staticflickr.com/1464/24962527114_f7e582daa5_b.jpg" /> <div class="grid__title"> <p>DSC_4585</p> </div> </div> <div class="grid__element" style="flex-basis: 336.449px; flex-grow: 1.68224;"> <img src="https://farm2.staticflickr.com/1587/25500055431_262305edbf_b.jpg" /> <div class="grid__title"> <p>rising sun by pfotograf</p> </div> </div> <div class="grid__element" style="flex-basis: 153.75px; flex-grow: 0.76875;"> <img src="https://farm2.staticflickr.com/1708/25225077389_0630066de1_b.jpg" /> <div class="grid__title"> <p>Bathed In Light</p> </div> </div> </div>
There’s a couple of things to pay attention to here. It’s also important to realise that this solution isn’t 100% perfect all the time. It depends a great deal about the images aspect ratio in relation to each other. If the difference between them is to great we might end up with a not perfect row.
Firstly, the way this works each row will fill up the entire width by default. This means that if the last row only contains one image we risk having it fill up the entire row. In some cases this might be ok, but for me I thought that looked odd. That’s where
$max-image-width in the scss file comes in. This makes sure no image gets to big and means the last rows images won’t fill up. This also means that if we have images with to different aspect ratios we might end up with images not filling up the entire rows before the last row. You’ll have to tweak this value to suit your needs.
max-image-width is to small we might end up with images hitting the max-width, this is not something we want because it breaks the flow.