How to Create Responsive Layout Using CSS

Finally, we come to the most important topic in responsive design, that is how to create a webpage layout that adapts to the viewport size.

In this lesson, we will create responsive page layouts that adapt to the viewport sizes using media queries, flexbox, grids, and other technologies.

For demonstration purposes, we are going to build a responsive page layout with a navigation bar, two sidebars (left and right), a main content section, as well as a footer, as shown in the diagram below.

page layout design

The HTML code is rather simple:

html
1<!DOCTYPE html>
2<html lang="en">
3  <head>
4    <meta charset="UTF-8" />
5    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6    <title>Flexbox Responsive Layout</title>
7
8    <link rel="stylesheet" href="style.css" />
9  </head>
10
11  <body>
12    <div class="container">
13      <div class="nav">Navbar</div>
14      <div class="side-primary">Primary sidebar</div>
15      <div class="content">
16        Main Content
17        <p>. . .</p>
18      </div>
19      <div class="side-secondary">Secondary sidebar</div>
20      <div class="footer">Footer</div>
21    </div>
22  </body>
23</html>

Before getting started with the layout, you should set box-sizing to border-box like we discussed previously. * is a wildcard selector that selects all elements in the HTML document.

css
1* {
2  box-sizing: border-box;
3  padding: 0px;
4  margin: 0px;
5}

We also added some decorative styles to make the webpage look better. You can create a different design if you want. These code will be omitted in future examples.

css
1div {
2  font-family: "Trebuchet MS", "Lucida Sans Unicode", "Lucida Grande",
3    "Lucida Sans", Arial, sans-serif;
4  padding: 20px;
5  text-align: center;
6  font-size: x-large;
7}
8
9p {
10  text-align: left;
11  font-size: medium;
12  font-family: Georgia, "Times New Roman", Times, serif;
13}
14
15.nav {
16  background-color: lightblue;
17}
18
19.side-primary {
20  background-color: lightcoral;
21}
22
23.content {
24  background-color: lightgreen;
25}
26
27.side-secondary {
28  background-color: lightsalmon;
29}
30
31.footer {
32  background-color: lightseagreen;
33}

Using flexbox

Next, you can create a flexbox layout by setting display to flex. The flexbox layout is, in fact, responsive by default when you set flex-direction to row and flex-wrap to wrap, since the next element will automatically occupy the next row when there is not enough space.

css
1.container {
2  display: flex;
3  flex-direction: row;
4  flex-wrap: wrap;
5}

Remember, we are following the mobile-first rule, so for small screens, each section should take up all the horizontal space available.

css
1.container > div {
2  width: 100%;
3}

⬇️ Flex layout on small screen

flex layout on small screen

For medium size screens, the setup is a bit more complicated but also more flexible. For instance, you can keep the navbar and footer to occupy 100% of the horizontal space. And then, let the sidebar and the main content section take the same row.

Here, in our example, the secondary sidebar will be hidden to make more space for the main section.

css
1@media screen and (min-width: 640px) {
2  .nav {
3    width: 100%;
4  }
5
6  .side-primary {
7    flex: 1;
8  }
9
10  .content {
11    flex: 3;
12  }
13
14  .side-secondary {
15    display: none;
16  }
17
18  .footer {
19    width: 100%;
20  }
21}

⬇️ Flex layout on medium screen

flex layout on medium screen

The flex property is a shorthand for flex-grow, flex-shrink, and flex-basis properties.

flex-grow controls how much an element grows compared to other elements as the viewport grows.

flex-shrink is the opposite. It defines how much an element shrinks as the viewport shrinks.

And lastly, flex-basis defines the initial size of the element.

The flex property has the following syntax:

css
1.element {
2  /* If one unitless value is given: flex-grow, flex-basis: 0; */
3  flex: 3;
4
5  /* If one value is given with unit: flex-basis */
6  flex: 10em;
7
8  /* Two values, unitless: flex-grow | flex-shrink */
9  flex: 3 3;
10
11  /* Two values, with unit: flex-grow | flex-basis */
12  flex: 3 100px;
13
14  /* Three values: flex-grow | flex-shrink | flex-basis */
15  flex: 2 2 10%;
16}

For larger screens, the secondary sidebar should also be displayed. It will take up the same amount of space as the primary sidebar.

css
1@media screen and (min-width: 1024px) {
2  .nav {
3    width: 100%;
4  }
5
6  .side-primary {
7    flex: 1;
8  }
9
10  .content {
11    flex: 3;
12  }
13
14  .side-secondary {
15    display: block;
16    flex: 1;
17  }
18
19  .footer {
20    width: 100%;
21  }
22}

⬇️ Flex layout on large screen

flex layout on large screen

Finally, let's see this layout in action:

flexbox layout

Using grids

It is also possible to create the same layout using a grid system instead of the flexbox. First, define the display type and create the columns using the grid-template-columns property.

css
1.container {
2  display: grid;
3  grid-template-columns: 1fr 1fr 1fr 1fr;
4}

Notice that we are using the unit fr, which means fraction. This example means we are dividing the page into four equal fractions, and each column takes one fraction. These relative units, such as %, vm, fr and others, are very useful for responsive web design.

On a small screen, all elements should take up all four columns:

css
1.nav {
2  grid-column: 1 / 5;
3}
4
5.side-primary {
6  grid-column: 1 / 5;
7}
8
9.content {
10  grid-column: 1 / 5;
11}
12
13.side-secondary {
14  grid-column: 1 / 5;
15}
16
17.footer {
18  grid-column: 1 / 5;
19}

On medium screens, the primary sidebar takes one column, and the main content section takes three.

css
1@media screen and (min-width: 640px) {
2  .nav {
3    grid-column: 1 / 5;
4  }
5
6  .side-primary {
7    grid-column: 1 / 2;
8  }
9
10  .content {
11    grid-column: 2 / 5;
12  }
13
14  .side-secondary {
15    display: none;
16  }
17
18  .footer {
19    grid-column: 1 / 5;
20  }
21}

Finally, on large screens, the sidebars each take one column, and the main content takes two.

css
1@media screen and (min-width: 1024px) {
2  .side-primary {
3    grid-column: 1 / 2;
4  }
5
6  .content {
7    grid-column: 2 / 4;
8  }
9
10  .side-secondary {
11    display: block;
12    grid-column: 4 / 5;
13  }
14}

Legacy layout method

Besides the flexbox and grid, there is also a legacy method called the grid view. This method divides the webpage into 12 columns, each taking 1/12 of the entire width. You can then decide how many columns an element should occupy for different viewports.

css
1.col-1 {
2  width: 8.33%;
3}
4.col-2 {
5  width: 16.66%;
6}
7.col-3 {
8  width: 25%;
9}
10.col-4 {
11  width: 33.33%;
12}
13.col-5 {
14  width: 41.66%;
15}
16.col-6 {
17  width: 50%;
18}
19.col-7 {
20  width: 58.33%;
21}
22.col-8 {
23  width: 66.66%;
24}
25.col-9 {
26  width: 75%;
27}
28.col-10 {
29  width: 83.33%;
30}
31.col-11 {
32  width: 91.66%;
33}
34.col-12 {
35  width: 100%;
36}

Create the same grid view for medium and large screens.

css
1@media screen and (min-width: 640px) {
2  .col-md-1 {
3    width: 8.33%;
4  }
5  .col-md-2 {
6    width: 16.66%;
7  }
8  .col-md-3 {
9    width: 25%;
10  }
11  .col-md-4 {
12    width: 33.33%;
13  }
14  .col-md-5 {
15    width: 41.66%;
16  }
17  .col-md-6 {
18    width: 50%;
19  }
20  .col-md-7 {
21    width: 58.33%;
22  }
23  .col-md-8 {
24    width: 66.66%;
25  }
26  .col-md-9 {
27    width: 75%;
28  }
29  .col-md-10 {
30    width: 83.33%;
31  }
32  .col-md-11 {
33    width: 91.66%;
34  }
35  .col-md-12 {
36    width: 100%;
37  }
38}
39
40@media screen and (min-width: 1024px) {
41  .col-lg-1 {
42    width: 8.33%;
43  }
44  .col-lg-2 {
45    width: 16.66%;
46  }
47  .col-lg-3 {
48    width: 25%;
49  }
50  .col-lg-4 {
51    width: 33.33%;
52  }
53  .col-lg-5 {
54    width: 41.66%;
55  }
56  .col-lg-6 {
57    width: 50%;
58  }
59  .col-lg-7 {
60    width: 58.33%;
61  }
62  .col-lg-8 {
63    width: 66.66%;
64  }
65  .col-lg-9 {
66    width: 75%;
67  }
68  .col-lg-10 {
69    width: 83.33%;
70  }
71  .col-lg-11 {
72    width: 91.66%;
73  }
74  .col-lg-12 {
75    width: 100%;
76  }
77}

Apply this layout to our HTML document.

html
1<body>
2  <div class="container">
3    <div class="nav col-12 col-md-12">Navbar</div>
4    <div class="side-primary col-12 col-md-4 col-lg-3">Primary sidebar</div>
5    <div class="content col-12 col-md-8 col-lg-6">
6      Main Content
7      <p>Lorem ipsum dolor sit. . .</p>
8    </div>
9    <div class="side-secondary col-12 col-md-12 col-lg-3">
10      Secondary sidebar
11    </div>
12    <div class="footer col-12">Footer</div>
13  </div>
14</body>

Take the primary sidebar as an example. On small screens, the element will take all 12 columns (col-12). On medium screens, the sidebar takes four columns (col-md-4), and on large screens, the sidebar takes only three columns (col-lg-3).

Responsive layouts without media queries

All three layout methods we just discussed are widely used in real life, even the legacy method, which is still supported by many popular CSS frameworks.

However, you may have noticed that it doesn't matter if you are using flexbox, grid, or the grid view. You will need to use media queries to create breakpoints for different devices.

Today, electronic devices come in many different sizes. In our example, we only created two breakpoints for medium and large devices, but in reality, you probably need many more breakpoints than that. So, is it possible for us to simplify this process and create responsive layouts without using media queries?

The answer is yes, but it does require some smart programming and an advanced understanding of CSS.

Say you are creating a webpage showing a list of articles using card components like this:

list of articles

html
1<h1>List of articles</h1>
2<div class="cards">
3  <div class="card">
4    <h2>Lorem ipsum dolor sit amet</h2>
5    <p>Sapiente eligendi nam . . .</p>
6  </div>
7  . . .
8</div>

First of all, let's think about how to create this list using grids. Assume each card is 20em wide, then you can automatically create the grid template using the repeat() function:

css
1.card {
2  border: 2px solid black;
3  border-radius: 5px;
4  padding: 10px;
5}
6
7.cards {
8  display: grid;
9  grid-template-columns: repeat(auto-fill, 20em);
10  grid-gap: 1em;
11}

The repeat() function creates a list of items based on the given values, which has the following syntax:

css
1repeat(<number_of_columns>, <column_size>)

In this example, the column size is 20em, and the number of columns is set to auto-fill, which means the browser will automatically find the maximum number of columns required to fill the entire row.

responsive layout without media query 20em wide

With this setup, you no longer need to use media queries, and the browser will automatically change the number of columns based on the viewport size.

However, this solution has one issue. Notice that when the viewport is not wide enough to fill an extra card, a very noticeable blank space will be left on the page.

In this case, you should ensure that the card also adapts as the viewport widens, instead of being fixed to 20em. This can be achieved using the minmax() function.

css
1.cards {
2  display: grid;
3  grid-template-columns: repeat(auto-fill, minmax(20em, 1fr));
4  grid-gap: 1em;
5}

The minmax() function gives a size that is greater than min (20em), and smaller than max (1fr) in order to fill the empty space.

responsive layout without media query grid layout

Alternatively, you can go with a flexbox layout.

responsive layout without media query flexbox layout