Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic number of columns, all centered, left-justified internally

Tags:

css

I have a number of tiles, all the same, static width and height inside some container. The width of the container is dynamic. The number of tiles is dynamic. I need the tiles to "wrap" such that as many as possible fit on each row but a new row is created when the number of tiles fills up the width of the container. A full row of tiles should be centered. A row of tiles that is not full should be left aligned with a full row (or positioned as if it was full and centered). See the diagram below.

Diagram 1

In this diagram, the green represents the container, the black represents the tiles.

I can get close by making the tiles inline-block and using text-align: center on the container. However, that comes out like the following instead:

Diagram 2

Here's a code sample. I am able to make necessary changes to the HTML.

.container {
  border: 5px solid #0f0;
  width: 250px;
  text-align: center;
}

.tile {
  margin: 3px;
  display: inline-block;
  border: 5px solid #000;
  width: 50px;
  height: 40px;
}
<div class="container">
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
</div>
like image 334
Nathan Wall Avatar asked Apr 11 '17 21:04

Nathan Wall


2 Answers

TL;DR

There is a way to do it with the CSS Grid Layout. One has to put the following properties to the parent element, which is the .container element in this scenario:

display: grid;
grid-template-columns: repeat(auto-fill, 60px);
grid-gap: 10px;
justify-content: center;

Some explanation and details

I've tried a lot to make this work, but in the end I was only successful with the CSS Grid Layout which I used for the first time here. I bet there are more readers that aren't very familiar with the above properties so I will add one or two lines of explanation for each.

display: grid;

There's not much to say about this, apart from the limitations in browser support.

grid-template-columns: repeat(auto-fill, 60px);

The grid-template-columns CSS property defines the line names and track sizing functions of the grid columns.

This property is a bit more complicated as there are many different options for its value. Here we use the repeat() function which requires the number of columns as first argument and the width of each column cell as second argument. Fortunately, it's possible to define a variable column number by using auto-fill. The width of 60px is the width of the .tile (50px for the width and 10px for the border).

grid-gap: 10px;

The grid-gap CSS property is a shorthand property for grid-row-gap and grid-column-gap specifying the gutters between grid rows and columns.

To define different gap widths for columns and rows you can use grid-gap: 5px 10px; where the first value is for the row-gap. I randomly selected 10px for this example.

justify-content: center;

Finally let's tell the browser to center the content of our container (the grid) if there is space around. This property is not related to the CSS Grid Layout. It is usually used in flexbox scenarios but works like a charm here as well.


Demo

I added the described four properties to the .container and removed the margin as well as the width from the .tile. Solution tested in Firefox 52 and Chrome 57.

.container {
  border: 5px solid #0f0;
  width: 250px;
  display: grid;
  grid-template-columns: repeat(auto-fill, 60px);
  grid-gap: 10px;
  justify-content: center;
}

.tile {
  display: inline-block;
  border: 5px solid #000;
  height: 40px;
}
<div class="container">
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
</div>

Nice to know

In the FF developer console (F12) there is a built-in grid highlighter that allows easy debugging of the defined grid:

Built-in grid highlighter in the FF developer console

like image 88
Marvin Avatar answered Nov 18 '22 07:11

Marvin


You could use flexbox, but if the last row doesn't have enough elements you'll have a similar issue as with inline-block.

You can use :before and :after to fill out the last row, but if more than 2 elements are missing from the last row it won't help entirely.

div {
  display: flex;
  align-items: stretch;
  justify-content: center;
  flex-wrap: wrap;
}

div > * {
  margin: 1rem;
}

div::before,
div::after {
  background: red; /* For demo only */
  display: block;
  content: "";
  width: 200px;
  height: 200px;
  margin: 1rem;
  order: 99999999;
}
<div>
<img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200"><img src="http://placehold.it/200x200">
</div>

Edit: Looking at my own example I realise this doesn't solve your problem. I'll delete this post unless it helps you somehow anyway.

Edit2: You may actually have to resolve to adding some empty divs using JS to fill out the void. That should work though I'd definitely consider it an ugly hack.

like image 2
powerbuoy Avatar answered Nov 18 '22 05:11

powerbuoy