Mobile HTML5 Tutorial – Building a Grid and Navigation System

HTML5 Mobile App Development for Beginners

Why should we consider mobile web design?

More and more users are accessing websites and shopping via their mobile devices everyday. A sample of these statistics are provided by Statistic Brain.

Mobile Web Design allows us to cater to mobile users in a way that will make their experience pleasant and encourage repeat visits.

There are many websites that use responsive design, but there are far more sites that use a fixed pixel layout. Viewing a website that uses a fixed pixel layout on a mobile device is painful and I think you will agree.

Before we dive into the ‘secret’ of good mobile web design, I want to point out a few things that you should take into consideration.

Note: When we talk about ‘screen size’ and ‘browser width’, we are talking about the viewport; ‘screen size’ referring to mobile devices and ‘browser width’ referring to desktops.

Annoyances

Pop ups
The easiest way to deter mobile users (or any web user) is to have some kind of pop-up when the page loads or another scenario where the user did not request this action.

Carousels
Carousels of images are a great way to load unnecessary data at the mobile user’s expense, to distract them from your content and waste precious screen real estate.

Limited internet
When you start to design mobile sites, always keep in mind that the user may or may not have a decent internet connection and that data packs are expensive, so we want to reduce both the downloaded file sizes and the page load time.

What does mobile design mean for desktop sites?

Just because we are designing for the mobile web, it doesn’t necessarily have to impact on the UX (User Experience) when a user views our site on a desktop.

The versatility of responsive web design allows our site to render gracefully in many browsers and platforms.

There is a difference between responsive web design and mobile web design. Most mobile web design is done using a responsive layout, but it is possible for a responsive site to not be mobile ready. We are about to look at two key features that all mobile web sites should entail.

Tutorial source code

You can get the source code of this tutorial here.

Setting the viewport

First of all we want to set the width of the viewport to the width of the device that the site is being view on.

We can do this with the viewport meta tag:

We have set the content attribute of the viewport meta tag to the width of the device and set it to an initial scale of 1.

Now that we have our viewport meta tag set up we are going to add some JavaScript to avoid a common annoyance when viewing a site on the iPad. This is something that can cause your site to automatically zoom in when your page loads, this is definitely something that we want to avoid.

Anti-zoom

We add the following snippet just before the closing head tag:

            if (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)) {
                var viewportmeta = document.querySelector('meta[name="viewport"]');
                if (viewportmeta) {
                   viewportmeta.content = 'width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0';
                    document.body.addEventListener('gesturestart', function () {
                        viewportmeta.content = 'width=device-width, minimum-scale=0.25, maximum-scale=1.6';
                    }, false);
                }
            }

This snippet of JavaScript will check if the user agent is an iPad or an iPhone. If it is an iPad or iPhone, it will create a variable that will ‘select’ our viewport meta tag.

The next statement will execute if the variable was created in the first if statement. If the variable was created, our viewport meta tag content will be set to:

width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0

As you can see, it sets the minimum, maximum and initial scale to 1.0- preventing the iPad from automatically zooming in on page load.

The snippet then adds an event listener to the body and listens for the ‘gesturestart’ event. Once the ‘gesturestart’ event fires, an anonymous function will run. This function will set the contents of the viewport meta tag to:

width=device-width, minimum-scale=0.25, maximum-scale=1.6

, allowing the user to still zoom in to the web page if they wish to.

Note: the gesturestart event fires as soon as a second finger is placed on the ‘iDevice’.

Media queries

The ‘secret’ to responsive web design.

This is also one of my favourite CSS modules.

Media queries allow us to specify a specific styling of an HTML element via a ‘condition’. When the ‘condition’ is satisfied, the style is applied.

The anatomy of a media query

            @media (condition) and (condition){
                selector {
                    // styles
                }
            }

An example of styling article elements when the browser width is between 480px and 540px inclusive:

            @media (min-width:480px) and (max-width: 540px){
                article {
                    // styles
                }
            }

An example of styling article elements when the browser width is greater than 767px:

            @media (min-width:768px){
                article {
                    // styles
                }
            }

Options

General practice would be to just include the media queries with the style sheet, after all it is CSS.

However, there are alternatives that are used commonly used.

Option 1:

You could have several different style sheets, all targeting different screen sizes and specify the media query in the link tag like so:

The downfall to this is that regardless of the media query condition, all of these style sheets will still be downloaded on page load and create unnecessary http requests.

Option 2:

You could also create two style sheets, one purely for media queries and the other for your default styling. You would then simply link to two different style sheets in your HTML.

Note: you would want to include the media queries after the default styling otherwise they could be overriden.

The main benefit to this technique is to completely separate the media queries from the default CSS. The downfall to this is that you would also be creating an unnecessary http request to download the two style sheets, rather than just the one.

Option 3:

You could create two websites and host one on a sub-domain titled ‘m’ and query the user agent when your site is loaded with something like this and redirect the user to m.yoursite.com if they are on a mobile device.

My preferred method is to combine all CSS (media queries and all) into one style sheet and place all media queries at the bottom of the style sheet.

By placing the media queries at the bottom of the style sheet, you can rest assured that your media queries will not be overridden by preceding CSS rules.

Breakpoints

Breakpoints are a fundamental understanding to working efficiently with media queries. Breakpoints refer to a specified width or height of the viewport in which a responsive website’s content will start to ‘break’- meaning that the content becomes ‘messed up’. We use media queries to alter the layout and style of the website at a ‘breakpoint’ to make sure that the site is reliable and looks best on most screen sizes.

Here are a few common media query conditions:

            240 to 319 - very small device
            320 to 479 - small device
            480 to 539 - slightly larger device
            540 to 767 - larger device
            768 to 999 - tablets
            1000 to 1200 - laptops
            1200+ - desktops

You may find that you require multiple media queries for your website or maybe just one. It is best practise to allow your content to dictate your media queries (i.e use media queries when you reach a ‘breakpoint), rather than try to target every single device that is being used in the modern world.

General Layout

As you may notice, sites that were built many years ago, are either designed with a 1024×768 or 800×600 width and height. The main reason being (I am sure you have guessed it) the resolution of the monitors used to view the websites.

Fixed Pixel and Fluid Layouts
Static sizes as the ones stated above are known as fixed-pixel layouts. As opposed to a fluid layout.

A fluid layout is simply a site that responds to its environment (screen size or browser width) and is more commonly known as responsive design.

A lot has changed since HTML was first invented in 1990.

Modern Layout

The standard width of a container is now 1200px. This is by no means a must, this is just a commonly used contained width. Your container could be anything you like.

Example of a container:

            .container{
              width: 100%;
              max-width: 1200px;
            }

The above example will fill 100% of the browser window, until the browser window is wider than 1200px; at this point the container will not be wider than 1200px.

A container is used to prevent the contained elements from straying.

The standard layout for a site when viewed on a mobile device is for the content to fill 100% of the screen horizontally; stacking our ‘sections’ on top of each other.

The Grid

Now that you have an understanding of basic mobile web design, let’s take a look at a common layout; the grid layout.

The Grid Layout is great and is a used in a lot of CSS frameworks.

It gives us freedom and a tonne of options when we start to add content to our websites.

Markup

We are going to create a grid layout with this simple, yet elegant HTML markup:

 

12

 

 

 

6

 

6

 

 

 

4

 

4

 

4

 

 

 

3

 

3

 

3

 

3

 

 

 

2

 

3

 

3

 

4

 

 

 

2

 

2

 

2

 

2

 

2

 

2

 

 

 

9

 

3

 

 

 

8

 

4

 

 

 

10

 

2

 

 

 

11

 

1

 

 

 

5

 

7

 

 

 

1

 

1

 

1

 

1

 

1

 

1

 

1

 

1

 

1

 

1

 

1

 

1

 

 

The CSS

Our grid layout will be comprised of twelve columns.

Twelve columns must be equal to 100%.

Widths

            /* 12 columns per row */

            .column.one     {width: 8.33333333333%;}
            .column.two     {width: 16.66666666666%;}
            .column.three   {width: 24.99999999997%;}
            .column.four    {width: 33.33333333334%;}
            .column.five    {width: 41.66666666667%;}
            .column.six     {width: 50%;}
            .column.seven   {width: 58.33333333335%}
            .column.eight   {width: 66.66666666668%;}
            .column.nine    {width: 75%;}
            .column.ten     {width: 83.33333333334%;}
            .column.eleven  {width: 91.66666666667%;}
            .column.twelve  {width: 100%;}

The above widths are just that; widths… We need to add some more CSS to get things cooking.

Box-sizing

One problem web developers face when creating a responsive layout is the box-sizing property in CSS. By default, the box-sizing property has a value of content-box. This is all well and good until you want to add some padding to an element; that’s where things start to get out of control. By setting the value of the box-sizing property using the universal (*) selector (it selects every element) to border-box, we avoid this issue.

            *{
                box-sizing: border-box;
            }

Rows

Next we want to setup our rows, which will be our containers. We want our rows to have a width of 1200px and a maximum width of 100%. We are also setting the top and bottom margins to 0px and the left and right margins to auto, so our grid is centered.

When we are on a device with a width of less than 768 (smaller than an iPad in Portrait Orrientation) we want to set the rows to have a width of auto, so the rows will fill the entire width of the viewport.

            .row{
                width: 1200px;
                max-width: 100%;  
                margin: 0 auto;
            }

            @media (max-width: 767px){
                .row{
                    width:auto;    
                }
            }

Note: !important will guarantee that our !important styles will not be overridden by any other CSS.

Columns

Now we should set up our columns.
We want our columns to float left, but for the last column within the parent element (.row in this case) we want to float right.

When we are on a device with a screen width of less than 768px, we want our columns to fill the parent element and not to float left or right.

We can do this by setting the width to auto and float to none.

I have also added some padding to the columns, purely aesthetic.

            .column{
                float:left;
                padding:15px;
            }

            .column:last-child{
                float: right;
            }

            @media (max-width: 767px){
                .column{
                    float:none !important;
                    width: auto !important;
                }
            }

Completed CSS

This is what the finished CSS should look like:

            *{
            box-sizing:border-box;
          }
          body{
            width:100%;
            margin:0 auto;
            font-family: Verdana, sans-serif;
          }
          .row{
            width: 1200px;
            max-width: 100%;  
            margin: 0 auto;
          }
          .column{
            float:left;
            padding:15px;
          }
          .column:last-child{
            float: right;
          }
          .box{
            background: #dddddd;
            border:1px solid #bbb;
          }    

          /* Grid Widths */
            .column.one     {width: 8.33333333333%;}
            .column.two     {width: 16.66666666666%;}
            .column.three   {width: 24.99999999997%;}
            .column.four    {width: 33.33333333334%;}
            .column.five    {width: 41.66666666667%;}
            .column.six     {width: 50%;}
            .column.seven   {width: 58.33333333335%}
            .column.eight   {width: 66.66666666668%;}
            .column.nine    {width: 75%;}
            .column.ten     {width: 83.33333333334%;}
            .column.eleven  {width: 91.66666666667%;}
            .column.twelve  {width: 100%;}

          /* Media Queries */
          @media (max-width: 767px){
            .row {
              width:auto;    
            }
            .column{
              float:none !important;
              width: auto !important;
            }
          }

I have added some styling, the body has a width of 100%, a margin that will center the content horizontally and have set the font to Verdana. The box class has also been added to help visualize the grid layout

The Finished Grid

            
            
                
                
                
                
                

                  *{
                    box-sizing:border-box;
                  }
                  body{
                    width:100%;
                    margin:0 auto;
                    font-family: Verdana, sans-serif;
                  }
                  .row{
                    width: 1200px;
                    max-width: 100%;  
                    margin: 0 auto;
                  }
                  .column{
                    float:left;
                    padding:15px;
                  }
                  .column:last-child{
                    float: right;
                  }
                  .box{
                    background: #dddddd;
                    border:1px solid #bbb;
                  }    

                  /* Grid Widths */
                        .column.one     {width: 8.33333333333%;}
                        .column.two     {width: 16.66666666666%;}
                        .column.three   {width: 24.99999999997%;}
                        .column.four    {width: 33.33333333334%;}
                        .column.five    {width: 41.66666666667%;}
                        .column.six     {width: 50%;}
                        .column.seven   {width: 58.33333333335%}
                        .column.eight   {width: 66.66666666668%;}
                        .column.nine    {width: 75%;}
                        .column.ten     {width: 83.33333333334%;}
                        .column.eleven  {width: 91.66666666667%;}
                        .column.twelve  {width: 100%;}

                  /* Media Queries */
                  @media (max-width: 767px){
                    .row {
                      width:auto;    
                    }
                    .column{
                      float:none !important;
                      width: auto !important;
                    }
                  }

if (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)) {
var viewportmeta = document.querySelector(‘meta[name=”viewport”]’);
if (viewportmeta) {
viewportmeta.content = ‘width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0’;
document.body.addEventListener(‘gesturestart’, function () {
viewportmeta.content = ‘width=device-width, minimum-scale=0.25, maximum-scale=1.6’;
}, false);
}
}

 

12

 

 

 

6

 

6

 

 

 

4

 

4

 

4

 

 

 

3

 

3

 

3

 

3

 

 

 

2

 

3

 

3

 

4

 

 

 

2

 

2

 

2

 

2

 

2

 

2

 

 

 

9

 

3

 

 

 

8

 

4

 

 

 

10

 

2

 

 

 

11

 

1

 

 

 

5

 

7

 

 

 

1

 

1

 

1

 

1

 

1

 

1

 

1

 

1

 

1

 

1

 

1

 

1

 

 

Now that you have your grid set up, we can discuss navigation for mobile devices.

Navigation

We are going to use this markup to create our mobile navigation:

Markup

 

 

 

Our navigation is made up of 3 nav elements:

#nav is the default navigational content that will display on devices of a screen of at least 768px.

#mobile-nav is the navigational content that will display on a screen size of less than 768px.

#nav-switch will also only be displayed on a screen size of less than 768px. #nav-switch is our ‘button’ that will open and close our mobile navigation.

CSS

Let’s start out with setting the box sizing property to border-box, changing the default font, setting our body width to 100% and giving it a margin of 0px for top and bottom and an auto value for left and right; this will center our content.

We are also going to reset some of the default values for lists in webkit browsers (Safari and Chrome), as we don’t want to have any additional padding or margins around our lists and setting the list style type to none; this will remove the default bullets (as this is an unordered list) from each list item.

            *{
                box-sizing: border-box;
                margin: 0;
                padding 0;
            }

            body{
                width: 100%;
                margin: 0 auto;
                font-family: Verdana, sans-serif;      
            }

            ul{
                -webkit-margin-before: 0;
                -webkit-margin-after: 0;
                -webkit-margin-start: 0;
                -webkit-margin-end: 0;
                -webkit-padding-start: 0;
                list-style-type: none;
            }

Now, we should add some styles that will apply to all of our navigation elements, by applying a background color, a text color, a width and text alignment.

            nav, nav#nav, nav#mobile-nav, nav#nav-switch{
                background: #222;
                text-align: center;
                width: 100%;
                color: #fff;
            }

Now to the anchor tags. We want to set these up so the mobile user has some room to click the links. If we don’t set these up right, the mobile user will have to be very accurate when clicking on a link and we don’t want to annoy them.

As anchor tags are inline elements, we can alter their default behaviour by changing their display property to block. We are also going to set the width to auto and give them a static height.

            nav a{
                text-decoration: none;
                color: inherit;
                display: block;
                width: auto;
                height: 30px;
                text-align: center;
            }

            nav a:hover{
                color: #ddd;
            }

We should also set some background and text color for the hover state of our list items and anchor tags within a nav element.

            nav ul li:hover{
                background: rgba(0,0,0,0.1);
            }

            nav ul li:hover a{
                color: #adadad;
            }

You may be wondering why there is a div surrounding the unordered list on our ‘desktop’ navigation. This is to stop our navigation from spanning 100% of the browser window, we want to keep our main content at a maximum of 1200px. We can also use this inner class to give our desktop navigation links a width of 25%.

            nav#nav .inner{
                max-width: 1200px;
                margin: 0 auto;
                text-align: center;
            }

            nav#nav .inner ul li{
                width: 25%;
            }

We want our main navigation (#nav) to float to the left, so it’s horizontally aligned, rather than vertically aligned and we want our unordered list within #mobile-nav to have a left margin of 5px; so it’s not squashed up against the left margin.

            nav#nav ul li{
                float: left;   
            }

            nav#mobile-nav ul{
                margin-left: 5px;
            }

Next we want to make sure that #nav-switch looks click-able. We can do this with the cursor property, setting it to pointer.

            nav#nav-switch{
                cursor: pointer;
            }

It is important to set the display properties of our nav elements, so we have the correct navigation content showing on the desired screen width.

By default, we want our #nav to be displayed and have a height set to auto. We then want our #mobile-nav and #nav-switch elements to not be displayed by default.

            nav#nav{
                display: block;
                height: auto;
            }

            nav#mobile-nav, nav#nav-switch{
                display: none;
            }

We then need to set up our media query to change these properties via our condition (a screen smaller than 768px or a screen larger than 767px).

            @media only screen and (min-width:768px){
                nav#mobile-nav{
                    display: none !important;
                }
            }

            @media only screen and (max-width: 767px){
                nav#nav{
                    display: none;
                }
                nav#nav-switch{
                    display: block;
                }
            }

Now we have our Styles set up, we can move on to the JavaScript.

JavaScript

An important thing to remember when designing for the mobile web is the toll that a large number of http requests being made and the toll that loading a large library or framework (which you may only require very few features from) has on your site.

Where possible, write your own javascript functions; this will ensure that you are not loading a large file in which you don’t intend to fully use.

We are going to write a javascript function that will turn our markup and CSS into a fully functional navigation system.

Firstly, we need to create some variables that will be references to out #mobile-nav and #nav-switch.

We can do this by using getElementById() and passing in the ids of our elements.

            var mobileNav = document.getElementById('mobile-nav');
            var navSwitch = document.getElementById('nav-switch');

We need to check the height of our #mobile-nav and see if it is displayed or not. We can do this with the offsetHeight property of our #mobile-nav in javascript, like so:

            if (mobileNav.offsetHeight <= 0) {
                // do stuff if mobileNav is 'hidden'
            } else{
                // do stuff if mobileNav is not 'hidden'
            }

By checking if the offsetHeight is less than or equal to 0, we know that our #mobile-nav element is not being displayed. So we can do some things that will make it appear and in the case that it is already displayed (else); we can ‘close’ our #mobile-nav. To make it easy for the user, we will also change the text displayed on the #nav-switch.

To make this #nav-switch work properly, our JavaScript will look something like this:

            if (mobileNav.offsetHeight <= 0) {
                mobileNav.style.display = "block";
                navSwitch.innerHTML = "CLOSE MENU";
                navSwitch.style.borderTop = "2px solid #ddd"; 
            } else{
                mobileNav.style.display = "none";
                navSwitch.innerHTML = "LAUNCH MENU";
                navSwitch.style.border = "0";
            }

Run down of the JavaScript

If our #mobile-nav is not currently displayed, when the function runs, we will set the display property of our #mobile-nav to block, we will change the text displayed on the #nav-switch to “CLOSE MENU” and set a border-top value (just to help separate the #nav-switch from the #mobile-nav).

Else (our #mobile-nav is currently displayed), when the function runs, we will set the display property of the #mobile-nav to none, change the text displayed on the #nav-switch to “LAUNCH MENU” and remove the border from the #nav-switch.

The Function

Now, to get this to ‘fire’ when we want, we wrap this in a function. I am going to name this function launchMenu.

            function launchMenu(){
                var mobileNav = document.getElementById('mobile-nav');
                var navSwitch = document.getElementById('nav-switch');

                if (mobileNav.offsetHeight <= 0) {
                    mobileNav.style.display = "block";
                    navSwitch.innerHTML = "CLOSE MENU";
                    navSwitch.style.borderTop = "2px solid #ddd";
                } else{
                    mobileNav.style.display = "none";
                    navSwitch.innerHTML = "LAUNCH MENU";
                    navSwitch.style.border = "0";
                }
            }

onclick

We will then need to attach an onclick event handler to our #nav-switch, so when it is clicked, our function will run.

Note: Keep in mind that our #nav-switch is ONLY displayed on a screen size smaller than 768px.

Complete Mobile Navigation

            
            
              
                
                
                
                

                  *{
                            box-sizing: border-box;
                    margin: 0;
                    padding 0;
                        }
                  body{
                            width: 100%;
                            margin: 0 auto;
                            font-family: Verdana, sans-serif;      
                        }
                  ul{
                            -webkit-margin-before: 0;
                            -webkit-margin-after: 0;
                            -webkit-margin-start: 0;
                            -webkit-margin-end: 0;
                            -webkit-padding-start: 0;
                    list-style-type: none;
                        }
                  nav a{
                            text-decoration: none;
                            color: inherit;
                            display: block;
                            width: auto;
                            height: 30px;
                    text-align: center;
                        }
                        nav a:hover{
                            color: #ddd;
                        }
                  nav{
                            width: 1200px;
                            max-width: 100%;
                            margin: 0 auto;
                            padding: 10px;
                  }
                  nav#nav ul{
                    height: auto;
                    height: 20px;
                        }
                        nav#nav ul li{
                            float: left;   
                        }
                        nav#mobile-nav ul{
                            margin-left: 5px;
                        }
                        nav ul li:hover{
                            background: rgba(0,0,0,0.1);
                        }
                        nav ul li:hover a{
                            color: #adadad;
                        }
                        nav, nav#nav, nav#mobile-nav, nav#nav-switch{
                            background: #222;
                            text-align: center;
                            width: 100%;
                            color: #fff;
                        }
                        nav#nav{
                            display:block;
                    height:auto;
                        }
                  nav#mobile-nav, nav#nav-switch{
                            display: none;
                        }         
                        nav#nav-switch{
                            cursor: pointer;
                        }
                  nav#nav .inner{
                            max-width: 1200px;
                            margin: 0 auto;
                    text-align: center;
                  }
                        nav#nav .inner ul li{
                            width: 25%;
                        }
                  @media only screen and (min-width:768px){
                            nav#mobile-nav{
                                display: none !important;
                    }
                        }
                  @media only screen and (max-width: 767px){
                            nav#nav{
                                display: none;
                            }
                            nav#nav-switch{
                                display: block;
                            }
                        }

if (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)) {
var viewportmeta = document.querySelector(‘meta[name=”viewport”]’);
if (viewportmeta) {
viewportmeta.content = ‘width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0’;
document.body.addEventListener(‘gesturestart’, function () {
viewportmeta.content = ‘width=device-width, minimum-scale=0.25, maximum-scale=1.6’;
}, false);
}
}

function launchMenu(){
var mobileNav = document.getElementById(‘mobile-nav’);
var navSwitch = document.getElementById(‘nav-switch’);

if (mobileNav.offsetHeight <= 0) {
mobileNav.style.display = “block”;
navSwitch.innerHTML = “CLOSE MENU”;
navSwitch.style.borderTop = “2px solid #ddd”;

} else{
mobileNav.style.display = “none”;
navSwitch.innerHTML = “LAUNCH MENU”;
navSwitch.style.border = “0”;
}
}

 

 

 

BONUS: Tips to Mobile Web Development

Minify your CSS and JS
When going into the production state of the design and development process it is always a good idea to minify your CSS and javascript where possible. A minifier will remove the white space and comments, providing you with a sufficiently smaller file size.

Compress your Images
When you do use images with your site, be sure to compress them. This process will provide a smaller file size for your images, without necessarily diminishing their quality.

The new standard for web image compression is WebP. Check it out at Google Dev.

Content first
Your users visit your site to consume content. Make it easy for them to do so, no matter what device they are using to view the site.

I hope you found this resource useful!