HTML and CSS course Β· Module 4: Flexbox

Creating a Grid with Flexbox

8 min read Β·
In this lesson10

You have photos of treasures from a tomb to show: several in a row on a computer, one below another on a phone. The ancient Egyptians were masters of planning space, from the symmetrical columns of their temples to the precise division of fields along the Nile. Creating a grid with Flexbox is the modern equivalent of that skill. We will go step by step through building a responsive layout with flexible columns whose widths are set in percentages, similar to what frameworks such as Bootstrap or Tailwind CSS do.

A short history of page layouts

In the 1990s, page layouts were built from HTML tables, even though <table> is meant for data. In the 2000s, float and clear, which you know from the CSS location, ruled the day. Flexbox, whose specification took its current form in 2012, finally made it possible to place elements in a row without tricks, and in 2017 browsers shipped CSS Grid, designed specifically for two-dimensional grids. The Flexbox grid is still alive, for example in Bootstrap since version 4.

Two properties the grid stands on

A grid is built on two container properties: display: flex places the elements in a row, and flex-wrap: wrap lets them move to a new line. The rest is four steps in a fixed order: turn on display: flex, add flex-wrap: wrap, set the column widths, for example flex-basis: 33%, and finally add gap for spacing.

Step 1: Create a Basic HTML Document Structure

First, create a new HTML file and add the basic document structure. Add a <div> element with the .flex-grid class and several <div> elements with the .col class, which will represent the columns of the grid:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="styles.css"> <!-- Link CSS file -->
  <title>Creating a Grid with Flexbox</title>
</head>
<body>
  <div class="flex-grid">
    <div class="col col-6">Column 1</div>
    <div class="col col-6">Column 2</div>
  </div>
  <div class="flex-grid">
    <div class="col col-4">Column 1</div>
    <div class="col col-4">Column 2</div>
    <div class="col col-4">Column 3</div>
  </div>
  <div class="flex-grid">
    <div class="col col-3">Column 1</div>
    <div class="col col-3">Column 2</div>
    <div class="col col-3">Column 3</div>
    <div class="col col-3">Column 4</div>
  </div>
</body>
</html>

Each row is a separate .flex-grid. The columns share the col class and have a numbered class, such as col-6, which says how many of the twelve parts of the width the column takes. In every row the numbers add up to 12.

Step 2: Create a CSS File

Open or create a new CSS file and save it as styles.css. This is the file the <link> tag from step 1 points to.

Step 3: Basic Styling

Add basic styles for the .flex-grid and .col elements so that they are visible on the page. This is where the grid gets its two key properties:

body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  gap: 20px;
  min-height: 100vh;
  background-color: #f0f0f0;
}

.flex-grid {
  display: flex;
  flex-wrap: wrap;
  width: 80%;
  background-color: #fff;
  border: 1px solid #ddd;
  padding: 10px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  gap: 10px;
}

.col {
  background-color: lightblue;
  padding: 20px;
  text-align: center;
  border: 1px solid #ccc;
}

body is a flex container laid out as a column, stacking the rows one below another. Every .flex-grid has display: flex, flex-wrap: wrap and gap: 10px, and .col gets a background, padding and a border so you can see it.

Step 4: Creating Column Classes with Percentage Values

Add styles for the classes from .col-1 to .col-12, which set the column widths in percentages. The formula is simple: the number in the class name divided by 12:

.col-1 {
  flex-basis: 8.33333333%;
  max-width: 8.33333333%;
}

.col-2 {
  flex-basis: 16.66666667%;
  max-width: 16.66666667%;
}

.col-3 {
  flex-basis: 25%;
  max-width: 25%;
}

.col-4 {
  flex-basis: 33.33333333%;
  max-width: 33.33333333%;
}

.col-5 {
  flex-basis: 41.66666667%;
  max-width: 41.66666667%;
}

.col-6 {
  flex-basis: 50%;
  max-width: 50%;
}

.col-7 {
  flex-basis: 58.33333333%;
  max-width: 58.33333333%;
}

.col-8 {
  flex-basis: 66.66666667%;
  max-width: 66.66666667%;
}

.col-9 {
  flex-basis: 75%;
  max-width: 75%;
}

.col-10 {
  flex-basis: 83.33333333%;
  max-width: 83.33333333%;
}

.col-11 {
  flex-basis: 91.66666667%;
  max-width: 91.66666667%;
}

.col-12 {
  flex-basis: 100%;
  max-width: 100%;
}

flex-basis sets the starting width of a column, and max-width works as a safety catch in case someone adds flex-grow to a column. Twelve parts were chosen on purpose, because 12 divides by 2, 3, 4 and 6. If you do not want to calculate, leave some room: three columns with flex-basis: 30% will fit in a row with typical gaps.

Step 5: Responsive Columns

To make the grid responsive, we can use media queries and change Flexbox properties depending on the screen width:

@media (max-width: 768px) {
  .flex-grid {
    flex-direction: column;
  }

  .col {
    flex: none;
    width: 100%;
  }
}

On screens up to 768 pixels, a row stacks its columns on top of each other, and flex: none with width: 100% are meant to stretch them to the full width.

The trap: padding, borders and gaps

When you open the page, a surprise awaits you: two col-6 columns will not sit side by side. By default, flex-basis and max-width measure only the content of a column, so the padding and the border are added on top of the 50%, and the gap comes on top of that. The total exceeds 100%, and the second column drops to a new line. On a phone, in turn, the max-width from the column classes still applies. Three changes added at the end of styles.css fix this:

*,
*::before,
*::after {
  box-sizing: border-box;
}

.col-3 { flex-basis: calc(25% - 7.5px); max-width: calc(25% - 7.5px); }
.col-4 { flex-basis: calc(33.333% - 6.667px); max-width: calc(33.333% - 6.667px); }
.col-6 { flex-basis: calc(50% - 5px); max-width: calc(50% - 5px); }

@media (max-width: 768px) {
  .col {
    max-width: 100%;
  }
}

box-sizing: border-box, which you know from the box model lesson, includes the padding and the border in the width. From each column we subtract its share of the gaps: for example, two col-6 columns share one gap, so each gives up 5 pixels. On a phone, max-width: 100% removes the limit. You will learn the formula for such calculations in the lesson about gap.

Result

After completing steps 1 to 5, your files should look like this. Add the fixes from the previous section at the very end of the stylesheet so that they override the earlier rules.

HTML File (index.html)

The HTML file stays unchanged, it is still three rows of columns:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="styles.css"> <!-- Link CSS file -->
  <title>Creating a Grid with Flexbox</title>
</head>
<body>
  <div class="flex-grid">
    <div class="col col-6">Column 1</div>
    <div class="col col-6">Column 2</div>
  </div>
  <div class="flex-grid">
    <div class="col col-4">Column 1</div>
    <div class="col col-4">Column 2</div>
    <div class="col col-4">Column 3</div>
  </div>
  <div class="flex-grid">
    <div class="col col-3">Column 1</div>
    <div class="col col-3">Column 2</div>
    <div class="col col-3">Column 3</div>
    <div class="col col-3">Column 4</div>
  </div>
</body>
</html>

CSS File (styles.css)

The stylesheet gathers all the rules from steps 3, 4 and 5 in one place, in the same order in which you wrote them:

body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  gap: 20px;
  min-height: 100vh;
  background-color: #f0f0f0;
}

.flex-grid {
  display: flex;
  flex-wrap: wrap;
  width: 80%;
  background-color: #fff;
  border: 1px solid #ddd;
  padding: 10px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  gap: 10px;
}

.col {
  background-color: lightblue;
  padding: 20px;
  text-align: center;
  border: 1px solid #ccc;
}

.col-1 {
  flex-basis: 8.33333333%;
  max-width: 8.33333333%;
}

.col-2 {
  flex-basis: 16.66666667%;
  max-width: 16.66666667%;
}

.col-3 {
  flex-basis: 25%;
  max-width: 25%;
}

.col-4 {
  flex-basis: 33.33333333%;
  max-width: 33.33333333%;
}

.col-5 {
  flex-basis: 41.66666667%;
  max-width: 41.66666667%;
}

.col-6 {
  flex-basis: 50%;
  max-width: 50%;
}

.col-7 {
  flex-basis: 58.33333333%;
  max-width: 58.33333333%;
}

.col-8 {
  flex-basis: 66.66666667%;
  max-width: 66.66666667%;
}

.col-9 {
  flex-basis: 75%;
  max-width: 75%;
}

.col-10 {
  flex-basis: 83.33333333%;
  max-width: 83.33333333%;
}

.col-11 {
  flex-basis: 91.66666667%;
  max-width: 91.66666667%;
}

.col-12 {
  flex-basis: 100%;
  max-width: 100%;
}

@media (max-width: 768px) {
  .flex-grid {
    flex-direction: column;
  }

  .col {
    flex: none;
    width: 100%;
  }
}

The order of the rules matters: the media query comes last, so on a phone it overrides the earlier column settings. For the same reason, the fixes go after it. My advice: when a layout needs rows and columns at the same time, reach for CSS Grid, and keep the Flexbox grid for simple rows.

Additional Task

  1. Add more columns and experiment with different flex-basis and max-width values to see how the grid layout changes.
  2. Test your skills by creating more complex layouts using different combinations of Flexbox properties.
  3. Add different styles to your grid, such as changing colors, font sizes, and other element properties to better understand how the different Flexbox options work.

Try the grid in the editor below. You will see it from a different angle: the containers have flex-wrap: wrap and a fixed height, so the elements form several lines, and align-content decides how those lines are distributed vertically.

Code for this lesson: index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flexbox - Align Content</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h1>Align Content - Distributing lines</h1>
        <p class="subtitle">Control the distribution of ROWS of elements in a container with flex-wrap</p>

        <section class="demo-section">
            <h2>align-content: flex-start</h2>
            <div class="flex-container align-start">
                <div class="flex-item">1</div>
                <div class="flex-item">2</div>
                <div class="flex-item">3</div>
                <div class="flex-item">4</div>
                <div class="flex-item">5</div>
                <div class="flex-item">6</div>
            </div>
            <p class="description">Lines are grouped at the start of the container</p>
        </section>

        <section class="demo-section">
            <h2>align-content: center</h2>
            <div class="flex-container align-center">
                <div class="flex-item">1</div>
                <div class="flex-item">2</div>
                <div class="flex-item">3</div>
                <div class="flex-item">4</div>
                <div class="flex-item">5</div>
                <div class="flex-item">6</div>
            </div>
            <p class="description">Lines are centered in the container</p>
        </section>

        <section class="demo-section">
            <h2>align-content: space-between</h2>
            <div class="flex-container align-between">
                <div class="flex-item">1</div>
                <div class="flex-item">2</div>
                <div class="flex-item">3</div>
                <div class="flex-item">4</div>
                <div class="flex-item">5</div>
                <div class="flex-item">6</div>
            </div>
            <p class="description">First line at top, last at bottom, equal spacing between</p>
        </section>

        <section class="demo-section">
            <h2>align-content: space-around</h2>
            <div class="flex-container align-around">
                <div class="flex-item">1</div>
                <div class="flex-item">2</div>
                <div class="flex-item">3</div>
                <div class="flex-item">4</div>
                <div class="flex-item">5</div>
                <div class="flex-item">6</div>
            </div>
            <p class="description">Equal spacing around each line</p>
        </section>

        <section class="demo-section practice">
            <h2>Practice: Egyptian treasure shelf</h2>
            <p class="practice-description">Arranging pharaoh's treasures on shelves</p>

            <div class="shelf-container">
                <div class="shelf">
                    <div class="treasure">β—†</div>
                    <div class="treasure">β—†</div>
                    <div class="treasure">β—†</div>
                    <div class="treasure">β—†</div>
                    <div class="treasure">β—†</div>
                    <div class="treasure">β—†</div>
                    <div class="treasure">β—†</div>
                    <div class="treasure">β—†</div>
                </div>
            </div>

            <div class="controls">
                <button onclick="changeAlign('flex-start')">Start</button>
                <button onclick="changeAlign('center')">Center</button>
                <button onclick="changeAlign('space-between')">Between</button>
                <button onclick="changeAlign('space-around')">Around</button>
            </div>
        </section>
    </div>

    <script>
        function changeAlign(value) {
            document.querySelector('.shelf').style.alignContent = value;
        }
    </script>
</body>
</html>

Remember: a Flexbox grid is a field by the Nile divided into twelve equal parts, where flex-wrap: wrap lets you start new rows and the percentages say how much land each column gets.

Docs entries used in this lesson

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. Which two properties are essential for creating a grid using Flexbox?

Hands-on tasks in the game

  • Vertical ordering

    Arrange the steps for creating a responsive grid with Flexbox:

  • Code editor

    Create a 3-column grid with 6 elements using display: flex, flex-wrap: wrap, and flex-basis: 30% on the child elements.

  • Click in order

    Arrange the shorthand flex declaration (grow: 1, shrink: 0, basis: 200px):

  • Vertical ordering

    Arrange CSS layout methods in chronological order:

Useful articles