Home > front end >  Responsive grid layout with perfectly squared cells
Responsive grid layout with perfectly squared cells

Time:01-10

I have a <section> element which must not be larger than 100vh and no wider than 100vw. Ideally it should follow its parent containers dimensions.

The <section> should have a grid layout using css variables; var(--cols, 56) and var(--rows, 32)

Each cell in the grid must be 1:1 ratio, meaning that the cell width must always === cell height.

The <section> element should grow if the viewport (parent element) grows, unless this makes the cells not square. If this is the case, the <section> element should be centered. So if the viewport width becomes larger and viewport height becomes smaller, the <section> is centered horizontally. But if the viewport height becomes larger and viewport width becomes smaller, the <section> element is centered vertically.

This is what I have so far:

<section #widgets>
  <ng-container *ngFor="let widget of widgetConfig">
    <ng-container [libTileContainer]="widget"></ng-container>
  </ng-container>
</section>
section {
    display: grid;
    gap: 5px;
    grid-template-columns: repeat(
      var(--cols, 56),
      calc(((100vw - (var(--cols, 56) * 5px) - 2rem) / (var(--cols, 56))))
    );
    grid-template-rows: repeat(
      var(--rows, 32), 
      calc(((100vw - (var(--cols, 56) * 5px) - 2rem) / (var(--cols, 56))))
    );
    max-width: 100%;
    max-height: 100%;
  }

This respects the cells squareness, but it bleeds out of it's parent. The calculations will also only respect viewport width and not height.

How can I achieve what I want?

CodePudding user response:

To set the height and width of grid items you might use a strategy that calculates both the height and width according to how many columns and rows are supposed to be fit inside the available space and then just use the lesser of the two on both the properties.

https://developer.mozilla.org/en-US/docs/Web/CSS/min

The min() CSS function lets you set the smallest (most negative) value from a list of comma-separated expressions as the value of a CSS property value.

In this demo I slightly refactored some choices:

  • I removed the default value for the number of columns to simplify the code.. anyway it could defined as an other custom property
  • I use the function min to determine which one is lower between the candidates height and width
  • I used place-content to center the items by default
  • I used grid-auto-rows to determine the size of rows because it's not important here to tell also how many
  • I told the div to take the whole width and height of their container

const items = 20;
const cols = 5;
const rows = 4;

initSection(items, cols, rows);

function initSection(items, cols, rows){
  const parent = document.querySelector('section');
  for(i=0;i<items;i  ){
    const item = document.createElement('div');
    item.innerText = i 1;
    parent.append(item);  
  }
  parent.dataset.col = cols;
  parent.dataset.col = rows;
}
:root{
  --cols: 56;
  --rows: 32;
}

*{
  box-sizing: border-box;
}

html, body{
  margin: 0;
  overflow: hidden; /*just to make sure the content doesn't overflow the viewport*/
}

section::before{
  /*this to show that the same dynamic strategy here worked instead*/
  --cols: attr(data-cols);
  --rows: attr(data-rows);
  
  position: absolute;    
  content: var(--cols) 'x' var(--rows);
  border: dashed 4px gray;
  padding: 0 .2em;
  font-size: 1.5rem;
  top: .2em;
  left: .4em;
}

section {  
  /*!this didn't work :(*/  
  /*
  --cols: attr(data-cols number, 56);
  --row: attr(data-rows number, 32);
  */
      
  --cols: 5;
  --rows: 4;  
  
  --gap: 5px;
  --col-size: calc(((100vw - (var(--cols) * var(--gap))) / (var(--cols))));  
  --row-size: calc(((100vh - (var(--rows) * var(--gap))) / (var(--rows))));    
  --size: min(var(--col-size), var(--row-size));
  
  display: grid;
  place-content: center;
  gap: var(--gap);
  grid-template-columns: repeat( var(--cols), var(--size));
  /*grid-template-rows: repeat( var(--rows), var(--size));*/
  grid-auto-rows: var(--size);
  width: 100vw;
  height: 100vh;      
}

section{
  border: solid red;
}

section div {  
  border: solid;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: 2rem;
}
<section data-cols="5" data-rows="4">    
  <!-- 
    <div>1</div>
    ...
  -->
</section>

  • Related