Logo Squares With CSS Grid

Whether it's for sponsors, donors, or product showcases, a logo grid is a common UI request. Placing differently sized logos into uniform squares gives the collection a clean structure, hiding the inevitable variation in aspect ratios and visual weights.

Building the Grid

If you use Emmet in your editor, you can generate all the grid markup for a set of logos with a quick abbreviation:

.grid>div*5>img

Pressing tab expands that into the full set of grid items:

<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>

Flexible Columns

The classic approach to a fluid number of columns relies on grid-template-columns with flexible sizing:

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  grid-gap: 1rem;
}

This technique gives you both flexible column widths and a flexible column count, letting the grid flow naturally without hardcoding a number of columns.

Making True Squares

For each grid item, we don't want to force a fixed height. Instead, we use the aspect-ratio trick: an empty pseudo-element with padding-bottom: 100% forces the item's height to match its width.

.grid > div {
  background: black;
  padding: 1rem;
}

.grid > div::before {
  content: "";
  padding-bottom: 100%;
  display: block;
}

With images temporarily hidden, the rectangles become perfect squares:

Overlapping the Content

When images are added, they don't nestle inside the square created by the pseudo-element. Instead, they expand the item, making it oblong again.

The typical fix with aspect-ratio tricks is absolute positioning of the inner content to cover the sized shape. Since we're already using grid for the outer layout, we can reuse it here. Making each grid item a grid container and placing both the pseudo-element and image in the same cell makes them overlap:

.grid > div {
  /* ... */
  display: grid;
}
.grid > div::before,
.grid > div > img {
  grid-area: 1 / 1 / 2 / 2;
}

That rule turns each grid item into its own grid, with no explicit rows or columns, and pins both children to the first row and column. The result is a proper overlay, restoring the square grid.

Centering the Logos

With real image sources in place and width: 100% set, images sit flush against the top of each item:

To center them, first stretch them vertically as well with height: 100%:

That distorts the logos, which is where object-fit steps in:

.grid > div > img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

The combination scales the artwork to fit within the square while preserving its original proportions and centering it in the available space:

The result responds cleanly to different viewport widths.

The Dragging Quirk

One subtle issue arises when a user tries to drag a logo out of the grid, e.g., to save the file. In that situation, the image behaves as if object-fit: contain were not applied, reverting to a plain width: 100%; height: 100% and looking distorted.

If avoiding that quirk matters for your use case, fall back to absolute positioning for the images inside the grid items. Assuming the grid child div is position: relative, this works:

.grid > div > img {
  position: absolute;
  max-width: 100%;
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
  margin: auto;
}

Another approach uses inset offsets:

.grid > div > img {
  position: absolute;
  max-width: 100%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

Either variant keeps the logos centered while fixing the drag behavior. This is a minor edge case, but worth knowing about if your logo grid is expected to let visitors save or inspect the artwork directly.