User's will be viewing your websites and web apps on devices of all shapes and sizes. While this is nice and convenient for them, it can cause massive headaches for us developers.

Luckily, with some handy CSS tools at our disposal, we can easily make responsive designs for all devices. Hurray!

Let's get stuck in. Here's what we are going to design:

As you can see we're making a recipe website (albeit not a very good one, since theres only 1 badly written recipe repeated 6 times. Anyway, moving on...)

A quick overview of our design:

  • On mobile screens, we want a single column, with our recipes stacked vertically. The banner should be hidden
  • On table screens we want to have a 2 column layout. The banner should be visible
  • On laptop/desktop screens, we want to have a 3 column layout. The banner should be visible

Start with mobile first

A best practice with UX is to design for mobile first, and add the tablet/desktop/laptop screens later. Why? Mobile has the most restrictions. You don't want to design an awesome website for desktop and then struggle to implement in on mobile later.

QUICK TIP: You can simulate a mobile view in Chrome by opening developer tools and clicking the phone icon:

With this is mind lets write some code.

Go ahead and create a folder (on your desktop or somewhere). Within that folder add 2 files: index.html and styles.css and a folder: images so that your directory looks like this:

Download the steak image from here (or use your own, it doesn't matter really) and add it to your images folder. Call it steak.jpg.

Open index.html and paste the following code:


    <!DOCTYPE html>
    <html>
    <title>My awesome food app</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="styles.css">

    <body>

    </body>

    </html>

Nothing overly exciting here, we're simply adding some basic HTML,  and linking our stylesheet. Although you should note the 5th line:

<meta name="viewport" content="width=device-width, initial-scale=1">

Ah! Our first glimpse into the world of responsive design.

The width=device-width part sets the width of the page to follow the screen-width of the device (which will vary depending on the device).

The initial-scale=1 part sets the initial zoom level when the page is first loaded by the browser.

Now let's add our banner. Within the body tags add the following:


	<div class="banner">
		<h2>My Awesome Food App</h2>
	</div>

Next, let's add a wrapper div that will hold our "cards (i.e recipes)":


	<div class="banner">
		<h2>My Awesome Food App</h2>
	</div>
	<div class="card-list-wrapper">

	</div>

QUICK TIP - Use a wrapper around "groups" of similar items to give you better control over how the items within the group are laid out, as we'll see shortly.

Lastly, let's add our "cards". Go ahead and paste the following code 6 times within the card-list-wrapper div:


		<div class="card">
			<img src="./images/steak.jpg" />
			<p>
				Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the
				industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type
				and
				scrambled it to make a type specimen book. It has survived not only five centuries, but also the
				leap
				into electronic typesetting, remaining essentially unchanged.
			</p>
		</div>


Your completed index.html file should look like this:


<!DOCTYPE html>
<html>
<title>My awesome food app</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">

<body>
	<div class="banner">
		<h2>My Awesome Food App</h2>
	</div>
	<div class="card-list-wrapper">
		<div class="card">
			<img src="./images/steak.jpg" />
			<p>
				Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the
				industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type
				and
				scrambled it to make a type specimen book. It has survived not only five centuries, but also the
				leap
				into electronic typesetting, remaining essentially unchanged.
			</p>
		</div>
		<div class="card">
			<img src="./images/steak.jpg" />
			<p>
				Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the
				industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type
				and
				scrambled it to make a type specimen book. It has survived not only five centuries, but also the
				leap
				into electronic typesetting, remaining essentially unchanged.
			</p>
		</div>
		<div class="card">
			<img src="./images/steak.jpg" />
			<p>
				Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the
				industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type
				and
				scrambled it to make a type specimen book. It has survived not only five centuries, but also the
				leap
				into electronic typesetting, remaining essentially unchanged.
			</p>
		</div>
		<div class="card">
			<img src="./images/steak.jpg" />
			<p>
				Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the
				industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type
				and
				scrambled it to make a type specimen book. It has survived not only five centuries, but also the
				leap
				into electronic typesetting, remaining essentially unchanged.
			</p>
		</div>
		<div class="card">
			<img src="./images/steak.jpg" />
			<p>
				Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the
				industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type
				and
				scrambled it to make a type specimen book. It has survived not only five centuries, but also the
				leap
				into electronic typesetting, remaining essentially unchanged.
			</p>
		</div>
		<div class="card">
			<img src="./images/steak.jpg" />
			<p>
				Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the
				industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type
				and
				scrambled it to make a type specimen book. It has survived not only five centuries, but also the
				leap
				into electronic typesetting, remaining essentially unchanged.
			</p>
		</div>
	</div>
</body>

</html>

If you open index.html in chrome or other web browser, you should see your new food recipe app - except it looks pretty terrible, so let's add some styles!

Hide the banner

Remember we are designing mobile first, and the banner is hidden on mobile. So let's start with that. Add the following to your styles.css


    .banner{
        background-color: beige;
        height: 50px;
        display: none;
    }

All we've done here is hidden the banner (using display:none) and given it a lovely UX friendly beige color! Lovely!

Make our image responsive

For mobile we want the image to take up the width of the screen (with a bit of margin to make it look nice), regardless of size. Add the following to styles.css


.card{
	margin: 10px;
}


img{
	width: 100%;
}

QUICK TIP - Using percentages makes the image resize based on screen size. If you want the image to stay the same size regardless of screen size, use pixels or other "fixed" value.

Set our single column layout

How do we do this? By using Flexbox to tell our wrapper to "Display all your child divs in a single column":


    .card-list-wrapper{
        display: flex;
        flex-direction: column;
        justify-content: center;
    }

That's it! Let's explain what's going on:

  • display: flex - means "the div with this class is a flex container"
  • flex-direction: column - means "display all the child divs in a single column". Remember our card-list-wrapper has 6 child divs
  • justify-content: center - means "center each of the child divs within their flex container"

Now, if you open index.html in the browser, and use the mobile tools (or resize the window to mobile size) you will see the cards flow in a nice column layout! Hurray!

However if you resize to iPad or desktop size, you'll notice we still have our single column view and the images are massive! How do we fix this?

Media queries to the rescue!

A media query is basically CSS code that says "only apply these styles if the condition is met". For example:

@media only screen and (min-width: 768px) has a condition of "only screen sizes with a minimum width of 768px.

Add the following to styles.css


@media only screen and (min-width: 768px){
	.card-list-wrapper{
		flex-direction: row;
		flex-wrap: wrap;
		max-width: 1200px;
		margin: auto;
	}

	.card{
		width: 350px;
	}

	.banner{
		display: block;
	}
}

What's happening here?

  • We've told our card-list-wrapper "For screens with a minimum of 768px, I want you to display your children in a row (i.e, flex-direction: row) and I want the row to wrap (i.e flex-wrap: wrap) when the row becomes too long"
  • We also want the card-list-wrapper to not go over 1200px, and we'll simply add margin: auto;. This prevents more than 3 columns and centers everything on extra large screens.
  • We've given the card div's a set width (i.e 350px). This is what enforces a line break. Example:

    If the user's browser width is 768px, the 3rd card will drop onto the next line (350px + 350px + 350px = 1050px, which is greater than 768px)
  • We're showing our banner

Since these styles are within a media query, they merge into or overwrite the other selectors. For example, within the media query, we didn't have to specify .card-list-wrapper as having display: flex, as we already specified it previously.

Your final styles.css should look like this:


body, h2{
	margin: 0;
}

.banner{
	background-color: beige;
	height: 50px;
	display: none;
}

.card-list-wrapper{
	display: flex;
	flex-direction: column;
	justify-content: center;
}

.card{
	margin: 10px;
}


img{
	width: 100%;
}

@media only screen and (min-width: 768px){
	.card-list-wrapper{
		flex-direction: row;
		flex-wrap: wrap;
		max-width: 1200px;
		margin: auto;
	}

	.card{
		width: 350px;
	}

	.banner{
		display: block;
	}
}

That's it! You've learned how to use basic media queries and flexbox to create a responsive column layout! Try experimenting with the code to try different things out.

Other resources

Flexbox: https://css-tricks.com/snippets/css/a-guide-to-flexbox/

Media queries: https://css-tricks.com/snippets/css/media-queries-for-standard-devices/

Flexbox froggy: https://flexboxfroggy.com