... fellow who started that whole “responsive design” thing.
— Ethan Marcotte
The term responsive design was coined by web designer Ethan Marcotte in his 2010 article entitled "Responsive Web Design" published in the online magazine A List Apart. In his article Ethan gave the ingredients (discussed later) necessary to achieve a responsive site.
Over the years the adoption of responsive design has sky rocketed due to a large percentage of users accessing the internet from their mobile phones. Today, most websites are shipped in responsive form (DEV included). Some of these sites are handcrafted from scratch and others employ frameworks to get the job done.
This post will show you the tools necessary to handcraft a responsive website from scratch using HTML and CSS. We'll talk about the recommended approach towards responsive site and we'll write code along the way to support all the theory.
The recommended approach can seem advanced as they contain tips and tricks to deliver a fast website by taking the end user into consideration but, we'll do our possible best to explain.
If you've followed this series up to this article, a very big thank you!. Mind you, here are some things you should know to get the most out of this post:
All screenshots are from Firefox 72.0.1 and its Developer Tools. One particular feature in Firefox 72.0.1 that's worthy of mention is the multi-line code editor in the console.
Let's dive in.
Designing a website is hard especially if you are handcrafting from scratch using HTML and CSS and at the same time you want to take the user experience into great consideration.
Reading the last paragraph you might ask: How can i take the user experience into consideration during design?
There are so many ways to design with the user in mind, among which is the subject of this article — responsive design — which revolves around designing your website to accommodate varying device screens and resolutions.
In his seminal article Ethan gave the three ingredients for responsive design. They are:
- Fluid grids
- Flexible images
- Media queries
Over the years the ingredients have evolved with the addition of the following:
- Responsive typography
- Use of relative units in the media queries
- Designing for content not device
From the list above the only thing that might seem alien to you is responsive typography. From a beginners standpoint, responsive typography is an advanced concept. We will not talk about it in this article, but, we'll write code to show how it works. A link will be given at end of the post if you would like to dive into the subject.
We've covered relative units when we discussed CSS units.
On the other hand, when you read "Designing for content not device", you might think this contradict the statement we gave earlier about designing for varying device screens and screen resolutions. Well the full statement should be:
Designing your site content for varying devices and screen resolutions.
Designing your site based on its content is part of a design approach known as device agnostic design. We'll take this approach in our code example.
Now you might ask: Why should i care about responsive design? Let's take three use cases.
The first use case → a user might be viewing your website on a smart phone and might decide to switch on auto rotation on their device due to changes in their sitting (or lying) positions which leads to changes in their device resolution as they browse your content.
When this happens they'll expect to still get the same browsing experience. If you did not take this kind of situation into consideration when designing your site and the site bails on them when they change their viewing resolution, they might leave and never come back.
The second use case is this → If your website is readable in the desktop version and difficult to read on mobile, the user might look else where.
For the third case, if you deliver a 2MB
image to a smaller device and the user happen to be on a slow internet connection, the image will take a while to download and they might leave before the download completes.
When you use responsive design in a responsible way you'll have done your best in delivering the best user experience imaginable. You don't need any special tool to achieve this because everything is available in native CSS.
That's it for the theory, let's write some code. Ready? Let's go!.
The following images are the different views of the example we'll be working on:
Our major concern is to explain the CSS bit by bit, so here is the whole HTML markup:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="author" content="Habdul Hazeez">
<title>Introdction to Responsive Design</title>
<link rel="stylesheet" type="text/css" href="responsive.css">
</head>
<body>
<!-- Header -->
<header class="header">
<h1 class="header__app-name c-white text-center">
<a href="">Responsive Design</a>
</h1>
<form class="search-form" action="" method="post">
<label for="search-site" class="hidden">Search site</label>
<input class="search-form__input" type="search" name="search" id="search-site" value="" placeholder="Search. e.g. JavaScript" required>
<button type="submit" value="Search" name="submit_search" class="search__button c-white">Search</button>
</form>
<nav class="navigation">
<ul class="navigation__menu">
<li class="navigation__item">
<a class="navigation__link" href="#">HTML</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">CSS</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">JavaScript</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">PHP</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">Design</a>
</li>
</ul>
</nav>
</header> <!-- ./Header -->
<div class="container">
<main class="blog-content clearfix"><!-- Blog Article -->
<article class="post">
<header class="post__header">
<div class="post__meta">
<h1 class="post__title">Introduction to CSS Block Formatting Context</h1>
<span class="post__author">
<strong>By:</strong> <a class="post__author-link" href="#">ziizium</a>
</span>
<span class="post__date"><strong>Published: </strong> 2019-10-02</span>
<span class="post__comment"><strong>Comments: </strong>0</span>
</div>
</header>
<div class="post__entry">
<p class="post__content">
Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmodtempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam,quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodoconsequat.
Duis aute irure dolor in reprehenderit in volupta...</p>
</div>
<div class="tag">
<ul>
<li class="tag__item">
<a class="tag__link" href="#">CSS</a>
</li>
</ul>
</div>
<div class="post__continue">
<a href="#" class="post__continue-reading">Continue Reading...</a>
</div>
</article> <!--./Blog Article -->
</main>
<aside class="sidebar desktop"> <!-- Sidebar -->
<div class="sidebar__container">
<h1 class="sidebar__heading c-white">Latest Post</h1>
<ol class="sidebar__menu">
<li class="sidebar__item">
<a class="sidebar__link" href="#">Introduction to CSS Block Formatting Context</a>
</li>
</ol>
</div>
<div class="sidebar__container">
<h1 class="sidebar__heading c-white">Archive</h1>
<ul class="sidebar__menu">
<li class="sidebar__item">
<a class="sidebar__link" href="#">October</a>
</li>
</ul>
</div>
</aside> <!--./Sidebar-->
<aside class="sidebar mobile"> <!-- Sidebar Mobile -->
<div class="sidebar__container">
<h1 class="sidebar__heading c-white">Latest Post</h1>
<ol class="sidebar__menu">
<li class="sidebar__item">
<a class="sidebar__link" href="#">Introduction to CSS Block Formatting Context</a>
</li>
</ol>
</div>
<div class="sidebar__container">
<h1 class="sidebar__heading c-white">Archive</h1>
<ul class="sidebar__menu">
<li class="sidebar__item">
<a class="sidebar__link" href="#">October</a>
</li>
</ul>
</div>
</aside> <!--./Sidebar-->
<div class="push"></div>
<div class="clearfix"></div>
</div> <!--./container-->
<!-- Footer -->
<footer class="footer c-white text-center">
<div class="social-media">
<li class="social-media__item"><a href="https://twitter.com/ziizium">Twitter</a></li>
<li class="social-media__item"><a href="https://github.com/ziizium">Github</a></li>
</div>
<div class="author-info">
<p class="author-info__content">A project by ziizium</p>
</div>
<div class="site-info">
<ul class="site-info__menu">
<li class="site-info__item"><a href="#">Contact</a>
</li>
</ul>
</div>
</footer> <!--./Footer-->
</body>
</html>
Observing the code above you'll notice we used the BEM naming methodology discussed in the article — CSS Naming Conventions.
You should also note that we've created a CSS file named responsive.css
. Kindly create your CSS file and give it your desired name and make sure it's linked with the HTML file.
When you take closer look at the <head>
section of the code, you will notice the following:
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
The first meta tag tells IE how to behave in regards to the value you set (IE=edge etc) if it is not there IE will show the site how it thinks best it should be shown. That could be compatibility mode or that could be the latest version of IE (source).
The second meta tag is telling the browser the following:
- The width of the content should be equal to the
viewport
(available browser window) - The content should scale to the browser window
- The browser should not shrink the content to fit the browser window
If you remember one of the use case discussed earlier you'll notice the first point in the list above should take care of it.
In our CSS file, the first thing we'll do is to reset the margin and padding for all elements on the page with the universal CSS selector (*
). Why are we doing this? The reason we are taking this approach is because of the browser styles applied to HTML elements by default.
When we do this, any margin values will be small unless we really need a big margin. If you load your HTML in the browser, you'll notice there are margins and paddings applied on all side on the page and block elements.
After resetting the margins and paddings we also change the box-sizing
property to border-box
. If you would like to know why we did this please refer to the post on CSS Box model.
The following CSS will take care of it:
/**
* Reset all margins and paddings
* and set box sizing to border-box
*/
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
Save your CSS and refresh your browser to see the changes.
Next, we set up variables for some repeated values. The variables will be applied in the global scope hence we use the :root
pseudo-class selector:
/*
* Variables for some repeated data
*/
:root {
--none: none;
--white: white;
--light-blue: hsl(210, 50%, 60%);
--light-grey: hsl(53.2, 4%, 41.2%);
--width-100: 100%;
}
Currently we are using the browser default font-family
and font-size
(Times New Roman
and 16px
), let's change that along with the page height
and a little aesthetics.
/**
* Set the font family to Georgia with "Trebuchet MS"
* and Verdana as backup options
*
* We also make use of responsive typography
* by performing some calculations using the
* calc() function available in CSS
*
* The height is set to 100% due to the approach
* we'll take in sticking the footer to the bottom
* of the page.
*
* The word-wrap property with the break-word value
* will allow word to break into multiple lines
*/
html,
body {
font-family: Georgia, "Trebuchet MS", Verdana, san-serif;
font-size: calc(13px + (22 - 16) * (100vw - 320px) / 1200);
height: 100%;
word-wrap: break-word;
}
Save your file and refresh your browser to notice the changes.
The next piece of code will remove the default styling for the HTML list and the text decoration
of the links. We also change the font-color
to white. Mind you, we are using the variables we set up earlier.
/**
* Remove the default styling for the lists
* and links.
*/
li {
list-style-type: var(--none);
}
a {
text-decoration: var(--none);
color: var(--white);
}
If you save and refresh your browser all the links will be "gone" due to their color that blends with the background color. Let's give the page header a background color among other things.
/**
* Here we change the background-color of
* the header to blue, we apply some paddings
* on all side and we give the app-name some
* margin-bottom to create some space
* between the app-name and the links
*/
.header {
background-color: #1560bd;
padding: 1.2em; /* Internal Spacing */
}
.header__app-name {
margin-bottom: 0.5em;
}
We are taking a mobile first approach, which means all styles so far will apply to mobile devices. At the same time we are designing for the content not the device.
The following styles is an attempt to make the search form user friendly and at the same time we are taking touch devices into consideration.
/**
* Styling the search form
*/
.search-form__input {
padding: 0.4em;
margin-right: 0.2em;
border: var(--none);
width: var(--width-100);
}
.search__button {
margin-top: 0.4em;
padding: 0.45em;
border: var(--none);
background-color: var(--light-blue);
width: var(--width-100);
}
.search__button:hover {
background-color: var(--light-grey);
cursor: pointer;
}
Save and refresh your browser, you'll notice the search button is now flat and the default border applied to form input are non existent. This is part of a design approach known as flat design.
Switch on Responsive Design Mode in your browser via the Developer Tools or with Ctrl + Shift + M
in Windows™. You can resize the browser window to a mobile view as depicted in the image below.
Now, gently resize the browser window until the header seems out of place. In our case it will be 768px
. We'll have to convert this value to em
to make the layout adapt easily.
The result will be 48em
(768/16
). We'll make use of this value in our media query.
/**
* The header looks out of place at about
* 768px which is equal to 48em
*/
@media screen and (min-width: 48em) { /* 768/16 = 48 */
/* The app name is floated left */
.header__app-name {
float: left;
margin-top: -0.2em; /* This will move the header down a little bit */
}
/* The search form is floated right */
.search-form {
float: right;
}
}
The navigation looks good on mobile, resize your browser to a certain point at which the navigation looks bigger than required, this will be a good break point. 560px
seems good, in em
unit this will be 35em
. We'll make changes at this point.
/**
* The navigation menu is transformed to a flex
* container, we remove the border and then add
* some padding.
*
*/
@media screen and (min-width: 35em) {
.navigation__menu {
display: flex; /* We change display to flexbox */
border: var(--none);
padding: 0 1em;
}
.navigation__item {
flex: 1;
margin-right: 1.2em;
}
/**
* We remove the border from the navigation
* then we add some padding and we align the
* text to the center
*/
.navigation__item:not(:first-child) {
border: 0;
}
.navigation__link {
padding: 0.3em;
text-align: center;
}
}
Resize your browser to a point the navigation comes crashing.
This should be another break point and it looks like the tablet layout i.e 768px
or 48em
. All we need is some margin and padding.
/**
* Our next break point for the navigation.
* We apply some margin and padding to sort
* of keep it in check.
*/
@media screen and (min-width: 48em) {
.navigation__menu {
margin-top: 3.5em; /* Outer spacing to bring it down */
padding: 0 4em; /* Padding applied to all four sides */
}
}
Save and refresh your browser, it should look better.
Our approach to get a sticky footer involves some extra markup (i know we can use flexbox). The extra markup will be an empty div
element with some height so that it can push the footer further downwards. We'll make use of the min-height
property with a value of 100%
to make sure the container occupy enough space.
/**
* Most of the code here allow us to create a sticky
* footer.
*/
.container {
margin-bottom: -3.125em; /* The height of the push element */
min-height: 100%; /* This make the sticky footer possible */
padding: 1em;
overflow: auto;
}
Save and refresh your browser. You'll notice the footer text moves to the bottom of the page.
The layout we are trying to achieve is a two column layout — blog content and a sidebar. Currently our sidebar is "invisible" due to the link colors blending with the background color.
/**
* The sidebar container is given a background color
* of blue with some margin and padding applied.
* We apply style to the heading and menu as decoration.
*
* In the process we make the sidebar links clickable
* on mobile.
*/
.sidebar__container {
background-color: #1560bd; /* A variant of blue */
margin-bottom: 0.5em;
padding: 0.7em;
}
.sidebar__heading {
font-size: 130%; /* Typography */
font-variant: small-caps; /* Typography */
margin-bottom: 0.5em;
}
.sidebar__menu {
font-size: 95%;
border: 1px solid #cccccc; /* A variant of silver */
}
.sidebar__item:not(:first-child) {
border-top: 1px solid #cccccc;
}
.sidebar__link {
display: block; /* Setup to make it clickable */
padding: 0.8em 1em; /* Creating the clickable area */
width: 100%;
}
.sidebar__link:hover {
text-decoration: underline; /* Visual effects */
background-color: var(--light-blue);
}
Save and refresh. You'll notice we have a problem in the layout. There are two sidebars, one meant for a desktop user and the other for a mobile user.
The solution is simple, all we have to do is hide the desktop sidebar on mobile and then hide the mobile sidebar on desktop (hacky! if you might say).
/**
* Allow the sidebar to take up much needed space
* then hide the desktop version.
*/
.sidebar {
width: 100%;
}
.sidebar.desktop {
display: var(--none); /* We are using CSS variables */
}
When you resize your browser to the tablet layout, the blog container and the sidebar appear on top of each other it will be a good idea to have the two column layout at this point.
/**
* We float the blog-content to the left
* and set its width to 70%. The overflow property
* is set to auto to prevent the content from
* overflowing.
*/
@media screen and (min-width: 48em) {
.blog-content {
float: left;
width: 70%;
margin-bottom: 1.2em;
overflow: auto;
}
/**
* Sidebar is floated to the right and given
* a width of 25%
*/
.sidebar {
float: right;
width: 25%;
}
/**
* Hide the mobile sidebar,then
* show the desktop sidebar
*/
.sidebar.mobile {
display: var(--none);
}
.sidebar.desktop {
display: block;
}
/**
* Remove the border-top property of the
* sidebar item
*/
.sidebar__item {
border-top: var(--none);
}
/**
* Reduce the padding of the sidebar links
*/
.sidebar__link {
padding: 0.2em 0.3em;
}
/**
* Remove the border from the sidebar
* in tablet and desktop mode
*/
.sidebar__menu {
border: var(--none);
}
}
Save and refresh your browser to see the changes.
Based on our design the post section has a border with some box-shadow
effect, let's implement that.
/**
* We add borders to the post area and some
* box shadow effect.
*
* The first value supplied to the box-shadow
* will be for the x axis (horizontal) and the second value
* is fr the y axis (vertical)
*/
.post {
border: 2px solid #1560bd;
box-shadow: -3px 3px #1560bd;
background-color: #f9f9fa;
margin: 0 0 1.5em 0.2em;
overflow: hidden;
}
The post metadata i.e the author name, date and comments are currently displayed in their default inline
position but, we need them to look good on mobile therefore we change their display
to block and we apply some paddings and margins.
/**
* Here we change the display property of the post
* author, date and comment to block
*/
.post__author,
.post__date,
.post__comment {
display: block;
margin: 0.6em 0 0.7em 0;
}
In the tablet and desktop version of the site we have to change the display back to inline
so that everything aligns on a single line.
/**
* We reset the display back to inline
* on tablet and desktop
*/
@media screen and (min-width: 48em) { /* Tablet upwards */
.post__author,
.post__date,
.post__comment {
display: inline;
}
}
You can save your file then resize the browser to see the changes.
The author link is currently invisible let's fix that.
/**
* Change the author link to blue
* and apply a border-bottom with a
* value of double
*/
.post__author-link {
color: #1560bd;
border-bottom: double;
}
Snap the display back to mobile. Now, if you take a look at the paragraph text you'll notice in its current form the text is almost unreadable. We have to increase the font-size
, apply a line-height
and some margins. We'll make use of the hyphens
property so that the browser can insert an hyphen at the end of a paragraph if the word breaks into another line.
We'll have to apply some margin-bottom
to the post meta, post title and post author to align them properly in desktop view.
/**
* Typography for the paragraph text and
* post meta data.
*/
.post__content {
margin: 0.5em 0 1.2em 0;
line-height: 1.618;
font-size: 1.2em;
hyphens: auto;
}
.post__meta,
.post__title {
margin-bottom: 0.7em;
}
.post__author {
margin-right: 0.5em;
}
We need to apply some internal spacing to the tag, post meta and post entry.
/**
* Internal spacing applied to the tag
* post meta data and post entry
*/
.tag,
.post__meta,
.post__entry {
padding: 0.5em;
}
The tag is currently not visible due to the link color. Let's take care of that.
/**
* Cosmetics for the tag. First we move the tag down
* a little bit and then customize the tag item
* and tag link
*/
.tag {
margin-top: 0.2em;
}
.tag__item {
display: inline-block;
text-transform: uppercase;
margin-right: 0.5em;
background-color: #1560bd;
padding: 0.8em;
margin-bottom: 1.5em;
}
.tag__link {
padding: 0.6em;
}
What's next? The continue reading link. The link has to be clickable on mobile. By applying some padding and width. The rest of the code are cosmetics to make it stand out.
/**
* Here we make sure the "continue reading" link
* is clickable and usable on mobile
*/
.post__continue-reading {
display: block; /* Setup to make it clickable */
padding: 0.6em; /* Creating the clickable area */
color: #1560bd;
background-color: #dddddd;
margin-top: 0.8em;
width: 100%;
text-align: center;
font-weight: bold;
border-top: 2px solid #1560bd;
}
.post__continue-reading:hover,
.post__continue-reading:focus { /* For human interaction */
text-decoration: underline;
background-color: #1560bd;
color: #ffffff;
}
For the desktop version of this design, we'll prefer the user make use of their mouse. The indicator that the user is interacting with the element will also change, this time to an underline.
The tag is also floated to the left and given a width of 50%
and the continue reading will occupy the remaining space.
/**
* On tablet and on desktop we change the
* "continue reading" link to a text the
* user click on by eliminating all the effects
* added for mobile users - padding and border.
*/
@media screen and (min-width: 48em) {
.post__continue-reading {
padding: 0;
border: var(--none);
background-color: #f9f9fa;;
text-align: right;
}
.post__continue-reading:hover {
text-decoration: underline; /* Serve as an indicator on mouse over */
background-color: #f9f9fa;
color: #1560bd;
}
/**
* The tag and post__continue container are
* floated left
*/
.tag,
.post__continue {
width: 50%
float: left;
}
/**
* A little bit of padding
*/
.tag__item {
padding: 0.15em;
}
}
Save and refresh your browser, you'll observe the footer is in contact with the sidebar in the mobile view. Remember, we are using additional markup to push the footer further down the page.
We've created the empty <div>
element in our HTML, what remains is the CSS. In our CSS, we'll give the <div>
an height
to get the space that'll separate the footer from the sidebar at all times.
/**
* The div in our HTML with a class pf push is
* given an height that'll push the footer further
* down the page.
*/
.push {
height: 3.125em;
}
We are almost done! The footer is next, the following snippet brings the footer to life.
/**
* The footer style, the code is quiet
* simple
*/
.footer {
background-color: hsl(53.2, 4%, 41.2%);
color: #ffffff; /* White */
overflow: auto;
padding: 1.25em;
margin-top: 1.2em;
}
/**
* All footer elements are given a margin bottom
* of 1.2em
*/
.footer * {
margin-bottom: 1.2em;
}
.footer a {
text-decoration: underline;
}
This works for mobile devices but looks out of fashion on desktop devices. All we need is a media query break point to change the layout.
/**
* We change the footer layout to a three
* column layout on tablet and desktop devices
*/
@media screen and (min-width: 32em) {
.footer div {
width: 33.33%;
float: left;
}
.social-media li {
display: inline;
margin-right: 1.2em;
}
}
Almost there! In our HTML we used some utility classes like c-white
(for white text), text-center
(align center) and hidden
(hide page element) now let's write the CSS.
Before that, we use float
for our layout, and due to the behavior of floated element, there will be issues when you add another element after the <main>
. Therefore a clearfix is needed.
The clearfix main ingredient is the CSS clear
property armed with pseudo-elements ::before
and ::after
we can ensure the addition of elements after <main>
without breaking the layout.
/**
* Utility classes
*/
.clearfix {
overflow: auto;
}
.clearfix::before,
.clearfix::after {
display: table;
content: " ";
}
.clearfix::after {
clear: both;
}
.c-white {
color: #ffffff;
}
.text-center {
text-align: center;
}
.hidden {
position: absolute;
text-indent: -9999px;
}
There you go, a fully responsive layout. Try and resize your browser manually to see how the layout adapt or better still switch on Responsive Design Mode in your browser.
For Firefox the shortcut is Ctrl + Shift + M
on Windows. The shortcut is the same for Chrome but, the Developer Tools has to opened and active for the shortcut to work.
Here are recommendations on designing a responsive site:
- Start with mobile layout
- Expand the browser window to a point the layout breaks, this would be your break point
When you follow the recommendations you'll be designing for content and not device. Therefore your layout will adapt as needed.
In addition:
- Use relative units like
em
in your media queries
I'll encourage you to break the code, then do your possible best to fix it. There is no better way to learn than solving your own problems.
Aside
We did not implement responsive images in our example but, we'll surely do that in our final project for this series.
In addition to that, if you'd like to know more on responsive typography kindly check out the following resource:
Our next topic is theoretical and it's about the layout design and some coding decisions for this example — Progressive Enhancement.
I'll see you then.