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.
The HTML code is rather simple:
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.
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.
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.
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.
1.container > div {
2 width: 100%;
3}
⬇️ 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.
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
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:
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.
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
Finally, let's see this layout in action:
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.
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:
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.
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.
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.
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.
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.
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:
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:
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:
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.
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.
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.
Alternatively, you can go with a flexbox layout.