37

I am trying to create grid/layout consists of squares. Four squares in each row. Squares can't distort on screen resize. Width and height must be the same all the time (I don't want fixed values). I must use CSS grid. Can anyone help me ?

.container {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
  grid-gap: 5px;
}
.container div {
  background-color: red;
}
<div class="container">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>
kukkuz
  • 41,512
  • 6
  • 59
  • 95
Minio
  • 371
  • 1
  • 3
  • 5

4 Answers4

89

With CSS only you could use a pseudoelement to keep always the aspect ratio to 1:1 or use the new aspect-ratio property , e.g.

.container {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
  grid-gap: 5px;
}
.container div {
  background-color: red;
  aspect-ratio: 1;
}
<div class="container">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>
Fabrizio Calderan
  • 120,726
  • 26
  • 164
  • 177
8

For the fun, curiosity of grid behavior and to avoid a pseudo element,

You may also set an height equal to the width of your grid container, the grid system will automaticly stretch the rows.

A good reminder to mind :

https://css-tricks.com/snippets/css/complete-guide-grid/

and examples https://gridbyexample.com/


working example if your grid uses entire browser's width

  * {
  margin: 0;
  padding: 0;
}

.container {
  display: grid;
  height: calc(50vw - 5px);  /*instead playing around with grid gap gap */
  grid-template-columns: 1fr 1fr 1fr 1fr;
}

.container div {
  /* bg to show i'm squarred or not ? */
  background-image: linear-gradient( 45deg, transparent 50%, rgba(0, 0, 0, 0.5) 50%);
 
 margin: 0 5px 5px 0;  /*instead playing around with grid gap gap */
  background-color: red;
}


/* extra for demo, not needed */

.container {
  counter-reset: test;
}

.container div {
  display: flex;  /* or grid */
}

.container div:before {
  counter-increment: test;
  content: 'Div N°:'counter(test)' -';
  margin: auto;/* center me */
  color: yellow;
<div class="container">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

A codepen to fork or play with

Community
  • 1
  • 1
G-Cyrillus
  • 101,410
  • 14
  • 105
  • 129
8

Another trick where you can put any content inside without breaking the ratio. The idea is to have the grid inside a big square that you divide into small squares:

.container {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: grid;
  grid-template-columns: repeat(4,minmax(0,1fr));
  grid-template-rows: repeat(4,minmax(0,1fr));
  grid-gap: 5px;
}

.container div {
  background-color: red;
}

body:before {
  content: "";
  display: block;
  padding-top: 100%;
}

body {
  position: relative;
  margin: 0;
}

img {
  max-width: 100%;
  max-height: 100%;
}
<div class="container">
  <div>any contet here</div>
  <div>any contet here any contet here</div>
  <div></div>
  <div></div>
  <div><img src="https://picsum.photos/200/300?image=1069"></div>
  <div></div>
  <div><img src="https://picsum.photos/200/200?image=1062"></div>
  <div>any contet here any contet here</div>
  <div>any contet here any contet here</div>
  <div></div>
  <div></div>
  <div><img src="https://picsum.photos/200/200?image=1062"></div>
  <div></div>
</div>

You can control the limit of rows. In the above I made them 4, we can have only 2 by making the padding 50% instead of 100%. We will have a big rectangle inside where will have 8 squares (4 in each row).

.container {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: grid;
  grid-template-columns: repeat(4,minmax(0,1fr));
  grid-template-rows: repeat(2,minmax(0,1fr));
  grid-gap: 5px;
}

.container div {
  background-color: red;
}

body:before {
  content: "";
  display:inline-block;
  padding-top: 50%;
}

body {
  position:relative;
  margin: 0;
}

img {
  max-width: 100%;
  max-height: 100%;
}
<div class="container">
  <div>any contet here</div>
  <div>any contet here any contet here</div>
  <div></div>
  <div></div>
  <div><img src="https://picsum.photos/200/300?image=1069"></div>
  <div></div>
  <div><img src="https://picsum.photos/200/200?image=1062"></div>
</div>

You can control the number of rows or columns using CSS variables and have something more generic:

:root {
  --n:6;
  --m:6;
}

.container {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: grid;
  grid-template-columns: repeat(var(--m),minmax(0,1fr));
  grid-template-rows: repeat(var(--n),minmax(0,1fr));
  grid-gap: 5px;
}

.container div {
  background-color: red;
}

body:before {
  content: "";
  display:inline-block;
  padding-top: calc(var(--n)/var(--m) * 100%);
}

body {
  position:relative;
}

img {
  max-width: 100%;
  max-height: 100%;
}
<div class="container">
  <div>any contet here</div>
  <div>any contet here any contet here</div>
  <div></div>
  <div></div>
  <div><img src="https://picsum.photos/200/300?image=1069"></div>
  <div></div>
  <div><img src="https://picsum.photos/200/200?image=1062"></div>
  <div>any contet here</div>
  <div>any contet here any contet here</div>
  <div></div>
  <div></div>
  <div><img src="https://picsum.photos/200/300?image=1069"></div>
  <div></div>
  <div><img src="https://picsum.photos/200/200?image=1062"></div>
  <div></div>
  <div></div>
  <div><img src="https://picsum.photos/200/300?image=1069"></div>
  <div></div>
  <div><img src="https://picsum.photos/200/200?image=1062"></div>
  <div>any contet here any contet here</div>
  <div></div>
  <div></div>
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Can you explain this code? `body:before { content: ""; padding-top: calc(var(--n)/var(--m) * 100%); }` – Fred Hors Mar 05 '19 at 22:22
  • @FredHors it allow to keep the ratio of the whole grid ... if we have the same number of rows and columns we should have a square thus it will be 100% in the padding because padding consider the width as reference and it will increase the height so height=width ... if we have n rows and m columns the ratio is n/m (no more square) so the height should (n/m)*width that's why the padding need to be n/m*100% – Temani Afif Mar 05 '19 at 22:26
  • These are not squares. – maracuja-juice Apr 21 '19 at 07:50
6

** @fcalderan's answer solves the issue and all credit reserved. **

This obviously breaks the square shape, but if you would be using any text a small adjustment will work in your favor. You could rather use the ::after pseudo element to not push down or split potential content. Changing to display: block also removes the necessity of vertical-aling: top as far as I know.

To further preserve the aspect ratio when using text, I'd make the text position: absolute.

See the snippet below when using ::before vs. ::after to illustrate my point.

.container,
.container2 {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
  grid-gap: 5px;
}
.container div {
  background-color: red;
}

.container div::before {
  content: "";
  padding-bottom: 100%;
  display: inline-block;
  vertical-align: top;
}

.container2 div::after {
  content: "";
  padding-bottom: 100%;
  display: block;
}

.container2 .text {
  position: absolute;  
}

.container2 div {
  background-color: green;
  position: relative;
  overflow: hidden;
}
<div class="container">
  <div>
    <div class="text">Here is some text.</div>
  </div>
  <div>
    <div class="text">Here is some more text.</div>
  </div>
  <div>
    <div class="text">Here is some longer text that will break how this looks.</div>
  </div>
</div>
  
  <hr>
  <div class="container2">
  <div>
    <div class="text">Here is some text.</div>
  </div>
  <div>
    <div class="text">Here is some more text.</div>
  </div>
  <div>
    <div class="text">Here is some longer text that will break how this looks.</div>
  </div>
</div>
guitarzero
  • 545
  • 9
  • 18