Home > Software engineering >  CSS display: grid; Create a grid of horizontally and vertically aligned squares that don't over
CSS display: grid; Create a grid of horizontally and vertically aligned squares that don't over

Time:01-10

Criteria

  1. The cells must keep their specified aspect ratio (1/1 in this case)
  2. The cells may not overflow the container
  3. The layout should work for any parent size and aspect ratio (eg: landscape, portrait, x/y)
  4. The cells must individually be centered (see example code below on what this means)
  5. In my case it has to be a 2*8 grid but the code should be flexible enough to handle any grid size

Here is a working solution that'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;
    border: 5px solid greenyellow;
    background-color: yellow;
    height: 25%;
    width: 50%;
    position: relative;
}
.inner {
    flex: 1;
    box-sizing: border-box;
    border: 5px solid red;
    background-color: purple;
    margin: auto;
    aspect-ratio: 1/1;
    max-height: 100%;
}
.content {
    box-sizing: border-box;
    background-color: blue;
    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 >1</div>
            </div>
        </div>
        <div >
            <div >
                <div >2</div>
            </div>
        </div>
        <div >
            <div >
                <div >3</div>
            </div>
        </div>
        <div >
            <div >
                <div >4</div>
            </div>
        </div>
        <div >
            <div >
                <div >5</div>
            </div>
        </div>
        <div >
            <div >
                <div >6</div>
            </div>
        </div>
        <div >
            <div >
                <div >7</div>
            </div>
        </div>
        <div >
            <div >
                <div >8</div>
            </div>
        </div>
    </div>
</body>
</html>

Here is the same snippet with less colors (more comprehensible)

.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 >1</div>
            </div>
        </div>
        <div >
            <div >
                <div >2</div>
            </div>
        </div>
        <div >
            <div >
                <div >3</div>
            </div>
        </div>
        <div >
            <div >
                <div >4</div>
            </div>
        </div>
        <div >
            <div >
                <div >5</div>
            </div>
        </div>
        <div >
            <div >
                <div >6</div>
            </div>
        </div>
        <div >
            <div >
                <div >7</div>
            </div>
        </div>
        <div >
            <div >
                <div >8</div>
            </div>
        </div>
    </div>
</body>
</html>

My current solution works perfectly (as in it produces the behavior that I want), but I have a few problems with it (in terms of code quality I find it to be a 'hacky solution').

  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 tring to learn display: grid; layout and this seems like a problem where the obvious choice would be grid but I've yet to come up with 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;

Is this even possible with purely grid or is the use of flex/table/whatever inevitable?

Here is my attempt at using grid

.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;
}
.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 >1</div>
            </div>
            <div >
                <div >2</div>
            </div>
            <div >
                <div >3</div>
            </div>
            <div >
                <div >4</div>
            </div>
            <div >
                <div >5</div>
            </div>
            <div >
                <div >6</div>
            </div>
            <div >
                <div >7</div>
            </div>
            <div >
                <div >8</div>
            </div>
        </div>
    </div>
</body>
</html>

The problem with this is that the 'content' class should be vertically aligned within the 'cell' class, and I was unable to achieve that, as the conventional ways of vertically aligning a div broke the layout.

CodePudding user response:

As per Mozilla 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>

  • Related