Home > Back-end >  Create a grid of horizontally and vertically aligned squares that don't overflow the container
Create a grid of horizontally and vertically aligned squares that don't overflow the container

Time:01-11

Criteria

  1. The cells may not overflow the container
  2. The layout should work for any parent size and aspect ratio (e.g., landscape, portrait, x/y)
  3. The cells must be centered within their row and their column (the content within the cell does not need to be centered)
  4. In my case it has to be a 2*8 grid but the code should be flexible enough to handle any grid size
  5. The cells should remain square upon resizing (keep their specified aspect ratio of 1/1)

Here is a working solution that I've managed to come up with (resize the container to see how it behaves on different aspect ratios)

.grid {
    resize: both;
    width: 100px;
    height: 200px;
    border: 3px solid black;
    overflow: hidden;

    display: flex;
    flex-wrap: wrap;
}
.cell {
    display: flex;
    box-sizing: border-box;
    height: 25%;
    width: 50%;
    position: relative;
}
.inner {
    flex: 1;
    box-sizing: border-box;
    margin: auto;
    aspect-ratio: 1/1;
    max-height: 100%;
}
.content {
    box-sizing: border-box;
    border: 3px  solid blue;
    background-color: #8080ff;
    aspect-ratio: 1/1;
    max-height: 100%;
    margin: auto;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div >
        <div >
            <div >
                <div ></div>
            </div>
        </div>
        <div >
            <div >
                <div ></div>
            </div>
        </div>
        <div >
            <div >
                <div ></div>
            </div>
        </div>
        <div >
            <div >
                <div ></div>
            </div>
        </div>
        <div >
            <div >
                <div ></div>
            </div>
        </div>
        <div >
            <div >
                <div ></div>
            </div>
        </div>
        <div >
            <div >
                <div ></div>
            </div>
        </div>
        <div >
            <div >
                <div ></div>
            </div>
        </div>
    </div>
</body>
</html>

My current solution is functional, but I have a few problems with it:

  1. There are too many wrapper elements (2 wrappers per cell). Ideally this should be reduced
  2. I would prefer a solution that uses display: grid;

I'm trying to learn display: grid; layout and this seems like a problem where the obvious choice would be grid, but I've yet to find a solution that uses it.

The solution I've provided is a reference implementation on how it should work. I'm looking for an implementation with the same behavior but one that uses display: grid;.

Here is my attempt at using grid layout:

.grid {
    border: 3px solid black;
    resize: both;
    overflow: hidden;
    width: 100px;
    height: 200px;

    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: repeat(4, 1fr);
}
.cell {
    box-sizing: border-box;
    border: 2px solid red;
    background-color: rgb(255, 161, 161);
    max-height: 100%;
    overflow: hidden;
}
.content {
    margin: auto;
    box-sizing: border-box;
    border: 5px solid blue;
    background-color: rgb(136, 136, 255);
    aspect-ratio: 1/1;
    max-height: 100%;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div >
        <div >
            <div >
                <div ></div>
            </div>
            <div >
                <div ></div>
            </div>
            <div >
                <div ></div>
            </div>
            <div >
                <div ></div>
            </div>
            <div >
                <div ></div>
            </div>
            <div >
                <div ></div>
            </div>
            <div >
                <div ></div>
            </div>
            <div >
                <div ></div>
            </div>
        </div>
    </div>
</body>
</html>

As you can see, my grid solution does not satisfy all of the criteria. The cells are not vertically aligned within their row. The conventional ways of vertically aligning a div broke the layout.

Is this even possible with a pure CSS grid layout or is the use of flex/table/whatever inevitable?

CodePudding user response:

As per MDN flex container documentation:

align-items: center;
justify-content: center;

can center vertically

.grid {
    border: 3px solid black;
    resize: both;
    overflow: hidden;
    width: 100px;
    height: 200px;

    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: repeat(4, 1fr);
}
.cell {
    box-sizing: border-box;
    border: 5px solid red;
    background-color: rgb(255, 161, 161);
    max-height: 100%;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div >
        <div >
            <div >
                1
            </div>
            <div >
                2
            </div>
            <div >
                3
            </div>
            <div >
                4
            </div>
            <div >
                5
            </div>
            <div >
                6
            </div>
            <div >
                7
            </div>
            <div >
                8<
            </div>
        </div>
    </div>
</body>
</html>

CodePudding user response:

I think this is simpler than you realize, and you're closer than you think. In the snippet below I've stripped out the unnecessary wrapper divs and added flexbox to center the cell content.

.grid {
  border: 3px solid black;
  resize: both;
  overflow: hidden;
  display: grid;
  max-width: 200px;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(4, 1fr);
}

.grid > * {
  margin: auto;
  box-sizing: border-box;
  border: 5px solid blue;
  background-color: rgb(136, 136, 255);
  aspect-ratio: 1;
  max-height: 100%;
  width: 100%;
  overflow: hidden;
  
  /* center the cell content in both directions */
  display: flex;
  justify-content: center;
  align-items: center;
}
<div >
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
  <div>7</div>
  <div>8</div>
</div>

CodePudding user response:

After hours of trial and error I've managed to come up with a solution.

No wrappers, no flexbox, no table, no container queries just a grid and its children

.grid {
    border: 3px solid black;
    resize: both;
    overflow: hidden;
    display: grid;
    width: 100px;
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: repeat(4, 1fr);
}

.cell {
    box-sizing: border-box;
    border: 5px solid blue;
    background-color: rgb(136, 136, 255);
    overflow: hidden;
    aspect-ratio: 1/1;
    
    /* keep the aspect ratio while filling as much space as possible */
    max-width: 100%;
    max-height: 100%;
    width: auto;
    height: auto;
    padding: 0;
    margin: 0;
    
    /* center the element */
    position: relative;
    top: 50%;
    left: 50%;
    transform: translate(-50%,-50%);
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div >
        <div ></div>
        <div ></div>
        <div ></div>
        <div ></div>
        <div ></div>
        <div ></div>
        <div ></div>
        <div ></div>
    </div>
</body>
</html>

CodePudding user response:

I am not clear why the grid is given any more than a width and the number of columns. Setting the rows seems redundant once we have set the height of cells in relation to their width (aspect-ratio: 1 / 1)

This snippet strips everything down to the bare essentials. Obviously if you need more structure to deal with whatever you want to put in a cell that can be added, but it will stay inside the cell and is not relevant to getting the basic grid layout that you want.

.grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  width: 100px;
  border: 3px solid black;
}

.grid>* {
  width: 100%;
  aspect-ratio: 1 / 1;
  border: solid blue 3px;
  box-sizing: border-box;
  background-color: #8080ff;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>

</head>

<body>
  <div >
    <div>
    </div>
    <div>
    </div>
    <div>
    </div>
    <div>
    </div>
    <div>
    </div>
    <div>
    </div>
    <div>
    </div>
    <div>
    </div>
  </div>
</body>

  • Related