Let’s build a literal grid of squares, and we’ll put the logos of some magazines centered inside each square. I imagine plenty of you have had to build a logo grid before. You can probably already picture it: an area of a site that lists the donors, sponsors, or that is showing off all the big fancy companies that use some product. Putting the logos into squares is a decent way of handling it, as it forces some clean structure amongst logos that are all different sizes, aspect ratios, and visual weights, which can get finicky and look sloppy.
By “grid” I mean CSS grid. Setting up a grid of (flexible) squares is a little trick of its own. Then we’ll plop those logos in there in a way that keeps them sized and centered. At the end, we’ll look at one little oddity.
1) Grid markup
Have you ever tried this in an editor that supports Emmet?
.grid>div*5>img
Then press tab. It will expand out into:
<div class="grid">
<div><img src="" alt=""></div>
<div><img src="" alt=""></div>
<div><img src="" alt=""></div>
<div><img src="" alt=""></div>
<div><img src="" alt=""></div>
</div>
Just a little trick for working quickly.
2) CSS Grid basics
We’ll use the infamous flexible columns technique:
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-gap: 1rem;
}
That will give us not only flexible columns, but a flexible number of columns. Remember, we don’t really care how many columns there are — we’re just building a grid of logos to display.
If we give each of those grid item divs a background and a forced height, we’d see something like:
2) Making real squares
Rather than force the grid items to any particular height, let’s use an aspect-ratio trick. We’ll plop an empty pseudo-element in there with padding-bottom: 100%;
meaning it will force the height to be at least as tall as it is wide.
.grid > div {
background: black;
padding: 1rem;
}
.grid > div::before {
content: "";
padding-bottom: 100%;
display: block;
}
If we temporarily hide the images, you’ll see the grid of rectangles has become a grid of perfect squares:
3) Overlapping the Images
But with the images in there, they grow a little oblong because the image sits in the pseudo-element.
We’ll need a way to sit them on top of one another. Usually, with aspect-ratio techniques, we reach for absolute positioning to place the in-child container to cover the now aspect-ratioed shape. But since we’re using grid already anyway, let’s use grid again to place the items into the same space:
.grid > div {
/* ... */
display: grid;
}
.grid > div::before,
.grid > div > img {
grid-area: 1 / 1 / 2 / 2;
}
That says to make the grid items of the main grid also into grid containers, with no explicit rows and columns, then to place both the pseudo-element and image onto the first row and column of that grid. This will force them to overlap them, making a nice grid of squares again.
4) Placing the images
Let’s plop a proper src
in there for each image. If we make sure the images fill the space (and limit themselves) with width: 100%
, we’ll see them along the top of the grid items:
Not terrible, but we would prefer to see them centered. Here’s one trick to do that. First, we’ll also make their height: 100%
, which distorts them:
Then fix that up with object-fit
!
.grid > div > img {
width: 100%;
height: 100%;
object-fit: contain;
}
There we go:
That will work responsively:
5) Quirky dragging size
This (probably) isn’t a massive deal, but notice how the logos look when you drag them off (like a user might if they are trying to save one):
The images look like they have width: 100%; height: 100%;
without the object-fit: contain;
.
Here’s the working demo so far, with that quirk:
6) Use absolute positioning instead
If that dragging quirk is a big deal, we can always just absolutely position the images inside the grid children instead.
Here’s one way, assuming the grid child div is position: relative;
:
.grid > div > img {
position: absolute;
max-width: 100%;
top: 0;
bottom: 0;
right: 0;
left: 0;
margin: auto;
}
And here’s another:
.grid > div > img {
position: absolute;
max-width: 100%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
Either of those fix that dragging quirk. Here’s a demo:
Video
If you’d like to watch a video walkthrough of all this, here ya go!
Great…. loved every piece of it.
I actually don’t have the stretching-while-dragging bug in Firefox. Might be a chrome thing
I don’t have it in the new Edge either. Odd
Re: .grid>div*5>img
Nice trick!
I put a little wobble on it and back lit on hover.
That’s a very interesting approach and a very practical solution.
I think though that the dragging quirk you are mentioning is something that is specific to Macs? I am seeing no distortion on Windows 10, using the latest versions of both Firefox and Chrome.
Thanks for sharing!
I really appreciate this, it really cool
Tech articles that dive deep into practical problems are a ton of fun. I would never think to even check the drag functionality, yet alone refactor my code for it. But it’s this attention to detail that’s needed for important projects with lots of users.
Why did you go for absolute positioning on the images, when you can fix this with just two lines of flexbox?
You could just use ‘display: flex; align-items: center;’ on the child divs, and the images will be centered perfectly. This solution is a lot cleaner imo.
I covered a couple different ways, especially in the video. Absolute positioning is one of them. Grid is another. Since the whole thing is grid, I prefer the the grid solution, but you’re right, flexbox is also capable of centering things nicely.
This wouldn’t work. The image need to be on top of the pseudo element
Very nice and so useful!
On a related note, I made some experiments back in February (based on code by Piper Haywood) to at least partially normalize the visual weight of logos with different aspect ratios. It sizes the images to have roughly comparable surface areas in px². You can see the demo with a more detailed explanation here:
https://nicksherman.com/size-by-area/
Just curious on the use of
<div>
s here. Wouldn’t it be more prudent/semantic to use a<ul>
and<li>
s for the grid items?Also, for me in Chrome (on Windows) the images drag at the same width/height as they are in the grid, so no weirdly exploding dimensions. Looks to be a browser quirk that one.
You certainly could use an unordered list! I guess that would announce how many logos are there to a screen reader user if you think that would matter to them. Probably better would be to ask your screen reader users if they think that is important to know. It doesn’t seem overly important to me, but I’m not a screen reader user. If the order of them mattered, you’ve got ordered lists as well.
Absolutely should use
<ul>
s and<li>
s. What we have here is a list of logos. They might be displayed as a grid, but it’s clearly a list. A user who can see will know immediately how many there are, as it’s visually obvious, and visual information is generally processed much, much faster than audio information is by people. A user relying on a screen reader will not, until their reader has got to the end and if they’ve kept count.Also, as it won’t affect the visual representation, it’s a zero-sum game.
would this work the same with svgs? — wondered why you didn’t use svgs as linked images (i.e. not inline) anyway rather than pngs ;)
Cool article, I have missed only one thing (something I had to deal with in the past).
These logos you were using are all wide and short. But what if you have to put logos of varied aspect ratios in a grid? Just imagine a few square-ish logos there, maybe some tall?
Just a heads up: the quirky way the images look when dragged doesn’t seem to happen in Firefox, so it’s not impossible it might be fixed in Chrome in the future!
This didn’t work with me!
my images become large and cover up the squares instead of filling them in
Hey, this is great!
Thanks for taking the time to put this together.
I am reading on CSS Grid, relativity new to this feature.
Is it possible to have the Grid and grid items expand to fill a given screen rather than having the white open space at the bottom of the grid?
This would be something similar to a Zoom App screen.
For example, if there are four logos then they will stretch symmetrically to fill the entire screen or if there were 1000, then they would do the same?
Is the above scenarios possible?
Thanks again!
Marc
Hey Marc! Yes, if you take out the
grid-gap
property from the.grid
element, then all of the logos will bump right up against each other. The secret sauce is really here:That tells the element to split itself into as many columns that fill the space, as long as each column is no smaller than 200px. Anything past 200px and the columns will share the space equally (1fr).
Hi, I wanted to do this, but I wanted the images to be clickable to go to a link. When I added the a href link tag ahead of the img tag it seemed to break the layout and stretch the icons