How to make square tiles in a grid grow and shrink evenly when the screen width changes

Viewed 917

I'm building a game which has the board on the left and the score area on the right. I'd like the individual tiles on the board to remain squares as the browser is expanded. As of now they grow width-wise but they don't grow in height.

Starting set up:

before expansion

But after making the window wider the tiles become rectangular: after expansion

How can I keep the tiles as squares when the browser window is resized?

Here's the relevant part of .html file:

<div class="board">
    <div class="square" id="square1">5</div>
    (...and 15 more like the above line...)
</div>
    
<div class="score">
    <h1>Score:</h1>
    <h2>0</h2>
</div>

JavaScript function that draws the tiles/squares:

function drawSquares() {
    for(let i = 1; i < 17; i++) {
        const square = document.createElement('div')
        square.setAttribute('class', 'square')
        square.setAttribute('id', ('square'+i))
        square.innerText = randArr[i-1]
        board.appendChild(square)
    }
}

and my full CSS as I'm not sure which part(s) to change:

body {
    display: flex;
}

.board {
    height: 370px;
    width: 70%;
    display: flex;
    flex-wrap: wrap;
    background-color: rgb(172, 170, 170);
    padding: 1%; 
    margin: 1%;
}

.score {
    background-color: rgb(172, 170, 170);
    margin: 1%;
    width: 30%;
} 

.square {
    height: 23%;
    width: 23%;
    background-color: rgb(121, 120, 117);
    margin-left: 1%;
    margin-right: 1%;
    justify-content: center;
    align-items: center;
    font-size: 5vh;
    color: rgb(121, 120, 117);
}

h1, h2 {
    text-align: center;
}

(The .square CSS selector styles the tiles)

2 Answers

Forcing a square (or any other) aspect ratio(s) of dynamically sized elements is one of the most difficult problems to solve with pure CSS. It is probably easier to use javascript to ensure the width/height ratio of the board element stays 1:1.

It would probably look something like this:

// Your example uses a class called board so maybe change it to an id or write your own js here:
let board = document.getElementById('board');

// Get width or height of board, whichever is smallest.
let size = Math.min(board.scrollHeight, board.scrollWidth);

// Set size of board.
board.style.width = size + 'px';
board.style.height = size + 'px';

Note that the above code snippet does not run again when the window is resized so you might want to put the board inside a container that grows to the maximum available space and take the smallest dimension of that and wrap the JS in a window.onresize event-listener.

To keep the aspect ratio in a responsive design, using the same height as width doesn't work as you saw. Height depends on the containing block having an explicit height set, but in a responsive grid, we don't want a fixed height.

Instead, you can use the padding as it will calculate the % of the container in the same way as the width. You can change the .square class to this:

.square {
    width: 23%;
    height:0;
    padding-bottom: 23%;
    /* Rest of your CSS... */
}

What this does:

  • We can set the padding-bottom to the same as the width - 23% in this case - to make the square
  • Set the height to 0 so that the padding-bottom is the only thing that is adding height

This gives you a fully responsive grid that keeps the same aspect ratio no matter what size the screen is.

Working Snippet:

function drawSquares() {
    for(let i = 1; i < 17; i++) {    
        const square = document.createElement('div')
        square.setAttribute('class', 'square')
        square.setAttribute('id', ('square'+i))
        square.innerText = [i];
        board = document.getElementsByClassName('board')
        board[0].appendChild(square)
    }
}
drawSquares();
body {
    display: flex;
}

.board {
    height: auto;
    width: 70%;
    display: flex;
    flex-wrap: wrap;
    background-color: rgb(172, 170, 170);
    padding: 1%; 
    margin: 1%;
}

.score {
    background-color: rgb(172, 170, 170);
    margin: 1%;
    width: 30%;
} 

.square {
    width: 23%;
    height:0;
    padding-bottom: 23%;
    background-color: rgb(121, 120, 117);
    margin-left: 1%;
    margin-right: 1%;
    margin-bottom: 2%;
    justify-content: center;
    align-items: center;
    font-size: 5vh;
    color: rgb(121, 120, 117);
    color: #fff;
}

h1, h2 {
    text-align: center;
}
<div class="board">
</div>
    
<div class="score">
    <h1>Score:</h1>
    <h2>0</h2>
</div>

In the example above, I also:

  • removed the height: 370px; fron your .board as it wasn't growing with the squares
  • added a bottom margin to the .squares to keep the spacing even
  • made the numbers white so I could verify that they didn't affect the size
Related