Video icon 64
Want to be a web designer? Skill up fast with video courses from Tuts+. Start your free trial.
Advertisement

Building the Responsive Timeline Portfolio Page

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →
This post is part of a series called Design and Build a Responsive Timeline Portfolio Page.
Design a Stylish Timeline Portfolio Page Using Photoshop
Finishing the Responsive Timeline Portfolio Page
Final product image
What You'll Be Creating

During this tutorial we will be building the fantastic Timeline Portfolio as seen in an earlier tutorial by Tomas Laurinavicius. We will be using some responsive techniques as well as CSS3 animations, Sass and a little bit of jQuery.

File and Directory Structure

OK, the first step is to create the files and folders we need. The image below shows our root structure.

As you can see, we have a very simple set up here. Inside the "css" directory go ahead and create a styles.scss file and also grab a copy of normalize.css. In order to use Sass in this project you will either need Sass installed on your machine or you will need an app to do the watching and compiling for you. I am currently using CodeKit for Mac, but there are plenty of alternatives such as Prepos, Scout, and Koala to name a few. They aren't all free, but whichever one you choose will save you a lot of time!

Inside the "js" folder create a file called app.js and download a copy of modernizr.js to place here as well. The Modernizr build I have used includes all CSS3 and HTML5 tests as we shouldn't need more than that. Right, that covers our initial files and folders. The next step is to check our PSD design to see which bits need slicing out.

Slicing the PSD

The design of this page is straight-forward which makes the job of deciding if we need any slices easier. Tomas has actually provided all of these assets along with the PSD, so it would be possible to have no slices at all from this design. However,  I decided for this tutorial we should slice out the three portfolio images and the loading icon at the bottom of the page. This is just for ease more than anything else, so we don't have to do any resizing in Photoshop.

In Photoshop grab the slice tool and carefully draw out the slices. You can name each slice by double-clicking on it. Then use the Save for Web... (or similar menu item depending on your version of Photoshop) to export the slices into our images directory.

I then created a new folder inside "images" called "portfolio" which will hold the images relating to the portfolio items. Move the three portfolio images into this folder and that completes our slicing process!

Avatar Image

Now head over to uifaces.com and pick one of the images to use as our profile image. I managed to find the same one as the design but it doesn't matter who you choose. Grab the 128x128 version and pop it in the "images" folder. 

We are nearly done with our images with only the social media and navigation icons to sort out. We will do that next.

Sprites

When you have groups of images like icons it is a good idea to create one image containing all of them in a grid pattern. These are called Sprites. You can take this to extremes and create one large file of every single image on your website, but for this tutorial we are going to create two sprites and a corresponding retina version for each.

So, using the asset links provided by Tomas, we can download each of the social media icons. Grab the 128x128 pixel version of each so we can scale down to the sizes we need. Then in Photoshop we need to create a file that is 101px by 51px. Drag each of the social media icons into this file and resize down to 24px by 24px. The icons are black, but we need them to be white, so apply a layer style to each one that gives a colour overlay of white. Change your background colour to something dark so we can see them and then arrange them like so...

Each icon is exactly 1 pixel from the edges and from each other. I also changed the opacity of each icon to 80%. Now duplicate this row of icons and place it directly below making sure to keep it 1 pixel from the edges. Change the opacity of the second row of icons to 100%. It should look something like this...

Now hide the background layer in Photoshop to give a transparent background and Save For Web as a PNG titled social-sprite.png inside the "images" folder. The next step is to create the retina version of this sprite, so we get crisp icons on high pixel density displays. This should be fairly simple and just involves doubling the size of what we have already. So the retina version will be 202px by 102px. Remember, everything has to be doubled which includes the spacing around each icon so for this sprite we will need 2 pixels between each icon and on the edges. The finished retina version should look like this...

Great! Now just hide the background layer like before and export as a PNG but this time call it social-sprite@2x.png. This is a standard naming convention for the retina version of an image file.

So now we just need to do the same with the navigation icons. These icons are vector objects inside the PSD which means we can just duplicate them into a new file and scale them as necessary. The size needed for the normal version is 49px by 18px and the retina version at double the size is 98px by 36px. The finished images should look like this:

Excellent! Save these as nav-sprite.png and nav-sprite@2x.png. I think that wraps up all we need to do for the images, so let's push on to writing some code!

The Base HTML and Sass

Let's begin with the barebones of our page.  In our index.html, copy the following markup to start things off:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="initial-scale=1.0">

    <title>My Portfolio</title>
    <link rel="stylesheet" href="css/style.css">
    <script src="js/modernizr.js"></script>
</head>
<body>
    <main class="wrap group">
        <aside class="sidebar">
           <div class="my-info">

           </div>
           <nav class="menus">

           </nav>
        </aside>
        <div class="content">

        </div>
    </main>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
    <script src="js/app.js"></script>
</body>
</html>

It's a simple layout with the main wrapping element containing a sidebar aside and a content div. Inside the sidebar we have two sections, my-info and a nav with a class of menus. Our dependencies are also all included, notably the latest version of jQuery 1.x. Let's now start filling out our Sass file with some variables.

$black: #000;
$white: #FFF;
$grey: #f7f7f7;
$darkgrey: #5e5e5e;
$red: #d35136;
$green: #27b599;
$blue: #088ecc;
$darkblue: #11171c;

$padding: 10px;
$margin: 10px;
$main-width: 1000px;

$font-size: 14px;
$font-family: 'Lato', sans-serif;
$line-height: 1.4;

$break-four: 1050px;
$break-three: 760px;
$break-two: 520px;
$break-one: 360px;

@import url('http://fonts.googleapis.com/css?family=Lato:300,400,700');
@import url('normalize.css');

//Utilities
.group:after {
  content: "";
  display: table;
  clear: both;
}

That's a fair chunk of code to start off with! This is really just setting up some defaults and variables for use throughout our Sass file. The most important section here is that which holds the four break point variables. These define at which points our layout should change. Sass comes in very handy here as we can just reference these variables when writing our media queries and if a break point needs to change there's only one place to change it. 

As you can see by the @import statement, we are also importing the Google font used in the design and the copy of Normalize.css we downloaded earlier. The only declaration so far is the group class which is a clearfix for elements that contain floated elements. Check out nicolasgallagher.com for more information on this.

If everything is set up correctly, saving styles.scss will generate the plain CSS file we have included in our page. Viewing the page now will show a rather boring white screen, so let's make it more fun by adding in some more styles and fleshing out the sidebar area.

The Sidebar; My Info

First of all let's add the following to our Sass file:

//Main styles
* {
    position: relative;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    -ms-box-sizing: border-box;
    box-sizing: border-box;
}

html, body, .wrap {
    min-height: 100%;
}

body {
    color: $grey;
    font-size: $font-size;
    font-family: $font-family;
    line-height: $line-height;
    background: $darkblue;
}

Every element should now be positioned relative and also have the box-sizing property set to border-box, allowing the elements to have percentage widths inclusive of their padding. This means we can set two elements next to each other of 50% width, then adjust the padding in each one as much we like without breaking the layout. A very, very useful CSS rule! Support for almost all of the browsers comes via the vendor prefixed declarations.

The remaining styles here act as defaults for our page. We have to make sure that the main wrapping elements remain at 100% height at all times as our sidebar needs to fill the screen vertically. We set the body background to $darkblue here, which is actually our sidebar colour. This is what gives our sidebar the appearance of 100% height. In reality, the sidebar element itself will only be as high as its content, but will have a transparent background, thus showing the colour behind it.

Let's start fleshing out the sidebar...

<img src="images/cj.jpg" alt="Chris Johnson" class="portfolio-image">
<h1>Chris Johnson</h1>
<h2>Experienced UX/UI <br> Designer based in London, UK</h2>
<div class="social group">
    <a href="#" class="dribbble">Dribbble</a>
    <a href="#" class="twitter">Twitter</a>
    <a href="#" class="facebook">Facebook</a>
    <a href="#" class="googleplus">Google+</a>
</div>

Add this code into the .my-info div. You may have to rename the portfolio-image depending on what you have called it. There's not an awful lot going on here, but save the file and take a look in the browser.

Lovely. Now open up style.scss and enter the following code:

.sidebar {
    width: 100%;
    height: 100%;

    @media screen and (min-width: $break-three) {
        float: left;
        width: 20%;
    }


    .my-info {
        text-align: center;
        padding: $padding*3 0;
        border-bottom: 1px solid $darkgrey;

        .portfolio-image {
            border-radius: 50%;
        }

        h1, h2 {
            font-weight: normal;
        }

        h1 {
            font-size: 120%;
        }

        h2 {
            font-size: 100%;
        }

    }

}

As we are following a mobile-first strategy here, we need our sidebar to be full-width and full-height at mobile resolutions. A quick media query for our $break-three break point floats the sidebar left and restricts the width to 20%. 

The styles for .my-info are simple enough. We just want to centre everything and give it all some space around the edges. To separate off our soon-to-be menu section we just use a 1px border on the bottom edge. To make the portfolio-image circular, give it a 50% border-radius and, lastly, we must override some font styles for any h1 or h2 elements inside this section.

Social Media Links

Now we need to tackle those social media links, using one of the sprites we created earlier. Place the following code directly after the h2 declarations in the last block of code.

.social {
    width: 120px;
    margin: 0 auto;

        a {
            float: left;
            width: 25px;
            height: 25px;
            margin: 0 $margin/5;
            background: url(../images/social-sprite.png) no-repeat;
            text-indent: -9999px;

            @media screen and (-webkit-min-device-pixel-ratio: 2), screen and (min-device-pixel-ratio: 2) {
                background: url(../images/social-sprite@2x.png) no-repeat;
                background-size: 101px 51px;
            }

            &.dribbble {
                background-position: 0px 0px;

                &:hover {
                    background-position: 0px -25px;
                }

            }

            &.twitter {
                background-position: -25px 0px;

                &:hover {
                    background-position: -25px -25px;
                }
            }

            &.facebook {
                background-position: -50px 0px;

                &:hover {
                    background-position: -50px -25px;
                }
            }

            &.googleplus {
                background-position: -75px 0px;

                &:hover {
                    background-position: -75px -25px;
                }
            }

        }
}

That looks quite complicated but it isn't really. The bulk of this code is handling how the icons and their hover states appear. First of all we must float each of the <a> elements, give them a width and height and also some margin to space them out. The next thing we do is specify the background image. This should be set to the social sprite we created earlier. Lastly, we have to make sure the text used in each element doesn't appear on the page. The text-indent property takes care of that nicely.

The next section is the interesting one. Here, we are specifying which background to use for high pixel density displays. The idea here is to use the "doubled up" version of the social sprite if the pixel ratio of the device is 2. The background-size property is necessary to scale the file back down to original size, so all of our positioning code just works without having to duplicate anything.

The next four sections are all the same concept. We are just setting the background position for each icon and the respective hover state. Let's take a look in the browser:

Awesome! That is looking really good. If everything is correct the hover states should all work and they should all look crisp on an iPhone/iPad etc. Now, let's start tackling the sidebar menu.

The Sidebar Menus

Start by entering the following into index.html in the menus container.

<h3 class="work">Work</h3>
<ul>
    <li><a href="#" class="current-menu-item">Latest</a></li>
    <li><a href="#">Web Design</a></li>
    <li><a href="#">Branding</a></li>
    <li><a href="#">Photography</a></li>
    <li><a href="#">Print</a></li>
    <li><a href="#">Mobile Design</a></li>
</ul>

<h3 class="about">About</h3>
<ul>
    <li><a href="#">Skills</a></li>
    <li><a href="#">Experience</a></li>
    <li><a href="#">Education</a></li>
    <li><a href="#">Clients</a></li>
    <li><a href="#">Testimonials</a></li>
    <li><a href="#">Blog</a></li>
</ul>

<h3 class="contact">Contact</h3>
<ul>
    <li><a href="#">Address</a></li>
    <li><a href="#">Phone</a></li>
    <li><a href="#">Social Networks</a></li>
    <li><a href="#">Email</a></li>
</ul>

Fairly self-explanatory I think, so let's carry on by entering the following styles after the my-info section, but still inside the overall sidebar element.

.menus {
    text-align: center;

    @media screen and (min-width: $break-three) {
        padding: $padding*2 $padding*3;
    }

    h3 {
        text-transform: uppercase;
        font-size: 120%;
        font-weight: normal;
        padding-left: $padding*2.5;
        cursor: pointer;
        width: 20%;
        margin: $margin*2 $margin*11 $margin;

        @media screen and (min-width: $break-one) {
            margin: $margin*2 auto $margin*2;
        }

        @media screen and (min-width: $break-three) {
            margin: $margin*2 0 $margin 0;
        }

        &:before {
            content: "";
            position: absolute;
            top: 0px;
            left: 0px;
            height: 18px;
            display: block;
            background: url(../images/nav-sprite.png) no-repeat;

            @media screen and (-webkit-min-device-pixel-ratio: 2), screen and (min-device-pixel-ratio: 2) {
                background: url(../images/nav-sprite@2x.png) no-repeat;
                background-size: 49px 18px;
            }
        }

        &.work {
            color: $red;

            &:before {
                width: 15px;
                background-position: 0px 0px;
            }
        }

        &.about {
            color: $green;

            &:before {
                width: 17px;
                background-position: -15px 0px;
            }
        }

        &.contact {
            color: $blue;

            &:before {
                width: 17px;
                background-position: -32px 0px;
            }
        }
    }
}

This is actually very similar to the social icons as far as the concept and the implementation of the concept. For the menus item itself we just want everything to be centred. Once we get above our $break-three break point we will need some padding to position the menus away from the edges slightly.

Each of the h3 elements should have different colours and different icons. We are using a combination of the :before pseudo element, sprites and media queries to achieve this. Aside from this we have a couple of media queries to control the margins of the h3 elements at larger screen sizes. This is so they are always positioned correctly in relation to their menus.

Speaking of the menus, we will add the CSS for them in a moment. First, let's take a look in the browser:

The titles look really nice! The menus not so much. Let's quickly take care of that before the blue defaults cause us any more pain...

Menu Lists

ul {
    list-style: none;
    padding: 0;
    display: none;
    margin: 0 $margin*13.5 $margin;
    text-align: left;
    
    @media screen and (min-width: $break-three) {
        margin: 0 0 0 $margin*2.5;
        display: block;
        width: auto;
    }

    &.open {
        display: inline-block;
        margin: 0 auto $margin $margin*6;

        @media screen and (min-width: $break-three) {
            margin: 0 0 0 $margin*2.5;
            display: block;
            width: auto;
        }
    }

    li {
        a {
            color: $darkgrey;
            text-decoration: none;
            -webkit-transition: color 0.4s ease;
            -moz-transition: color 0.4s ease;
            -o-transition: color 0.4s ease;
            -ms-transition: color 0.4s ease;
            transition: color 0.4s ease;

            &:hover, &.current-menu-item {
                color: $white;
                -webkit-transition: color 0.4s ease;
                -moz-transition: color 0.4s ease;
                -o-transition: color 0.4s ease;
                -ms-transition: color 0.4s ease;
                transition: color 0.4s ease;
            }
        }
    }
}

At mobile resolutions we want our menus to be hidden, so the user can choose to hide or show them when they need access to them. Once we hit our $break-three point the menus should be visible all the time. 

The .open class handles how the menus display when they are open. The design has them pretty precisely placed, so we do the same here using margins. Again, the $break-three point comes into play and overrides the margins for those resolutions and above. The styles for li elements are pretty straightforward. The main thing to note is the use of CSS3 Transitions. I've used them here to give a nice fade appearance when hovering on the element. I encourage you to play around with transitions to see what interesting effects you can achieve! 

Let's save the file and view the results in the browser.

Very nice! The menus are behaving exactly as they should. We're now going to carry out our first bit of JavaScript/jQuery, to control the opening and closing of the menus.

Open app.js and enter the following function:

$(function() {

    $('.menus h3').on('click', function(e) {
        $(this).next('ul').toggleClass('open');
        e.preventDefault(); return false;
    });

});

We are starting this file with the jQuery document ready function which basically waits for the document to be fully ready before running any of the code. Read more on the ready event on api.jquery.com.

We are then attaching a click event to any h3 inside our menus element. When this event happens (when the user clicks the h3) we use jQuery to find the "next" ul element and toggle the class of open on it. So if the element has the class already it will remove it, and vice versa if it doesn't. The final line is there to stop any default action for the element. This probably doesn't apply here because h3 elements don't have a default action, whereas an anchor tag, for example, does. Still, it is good practice to get into the habit of including it when using click event handlers.

Save this file and go back to the browser. When at mobile resolutions, if you click the menu titles you should see the menu open up below it. If this doesn't happen, check the JavaScript for any errors, or refresh your browser and try again.

That wraps up the sidebar area! Let's push forward and get some content onto the page.

Main Content

The first step here is to add the HTML we need to the page.

<article class="portfolio-item group first">
    <header class="portfolio-info">
        <div class="date">7 Nov 2013</div>
        <div class="description">
            Free PSD template for startups, small businesses and basically for everyone liking good design.
        </div>
        <div class="meta">
            <p><strong>Client:</strong> Despreneur</p>
            <p><strong>Tags:</strong> Web Design</p>
        </div>
    </header>
    <figure class="portfolio-image">
        <img src="images/portfolio/free-psd-templates.jpg" alt="Free PSD Templates">
    </figure>
</article>
<article class="portfolio-item group">
    <header class="portfolio-info">
        <div class="date">7 Nov 2013</div>
        <div class="description">
            Free PSD template for startups, small businesses and basically for everyone liking good design.
        </div>
        <div class="meta">
            <p><strong>Client:</strong> Despreneur</p>
            <p><strong>Tags:</strong> Web Design</p>
        </div>
    </header>
    <figure class="portfolio-image">
        <img src="images/portfolio/moody-shot.jpg" alt="Moody Shot">
    </figure>
</article>
<article class="portfolio-item group">
    <header class="portfolio-info">
        <div class="date">7 Nov 2013</div>
        <div class="description">
            Free PSD template for startups, small businesses and basically for everyone liking good design.
        </div>
        <div class="meta">
            <p><strong>Client:</strong> Despreneur</p>
            <p><strong>Tags:</strong> Web Design</p>
        </div>
    </header>
    <figure class="portfolio-image">
        <img src="images/portfolio/new-york.jpg" alt="New York">
    </figure>
</article>
<article class="portfolio-item group loading-wrap">
    <header class="portfolio-info">
    </header>
    <figure class="portfolio-image">
    <div class="loading">
        <img src="images/loading.png" alt="Loading" class="rotate">&nbsp;Loading...
    </div>
    </figure>
</article>

Place this code inside the .content div. We have the three articles that feature in the design and also a "Loading" article. I have placed the loading icon inside its own portfolio-item so we can keep the structure of the page. I can then just position the loading icon itself inside the portfolio-image element. You'll see here as well that the first portfolio-item has a class of first. This will be important in our CSS which we will get onto after taking another quick look in the browser:

Nice, but not quite what we want, so let's get straight into the styles.

.content {
    width: 100%;
    min-height: 100%;
    background: $white;

    @media screen and (min-width: $break-three) {
        float: left;
        width: 80%;
    }

    .portfolio-item {
        background: $grey;

        &:before {
            content: "";
            position: absolute;
            width: 3px;
            background: darken($grey, 5%);
            top: 0px;
            left: 17px;
            bottom: 0px;
        }

        &.first {
            &:before {
                top: 30px;
            }
        }

        .portfolio-info {
            min-height: 100%;
            color: $darkgrey;
            padding: $padding*2 $padding*2 $padding*2 $padding*4;
            -webkit-box-sizing: border-box;
            -moz-box-sizing: border-box;
            -ms-box-sizing: border-box;
            box-sizing: border-box;

            @media screen and (min-width: $break-three) {
                float: left;
                width: 30%;
            }

            .date {
                font-size: 110%;
                color: $black;
                margin-bottom: $margin;

                &:before {
                    content: "";
                    position: absolute;
                    width: 11px;
                    height: 11px;
                    border-radius: 50%;
                    border: 2px solid $grey;
                    background: $red;
                    left: -29px;
                    top: 3px;
                }
            }

            .meta {
                color: $black;
                margin-top: $margin;

                p {
                    margin: 0;
                }
            }

        }

    }

}

Another big chunk of code there! Let's break it down piece by piece. The .content styles are similar to the sidebar styles in that at mobile resolutions we just need it full width, but anything at or above our $break-three point we should float it and pull the width in to 80%. We also need to give the content div a white background to section it off from the sidebar.

The portfolio-item elements should have a grey background. To create the timeline running down the left edge of the content area we are going to give each portfolio-item:before element. Here we just absolutely position it 17px from the left edge and use the top/bottom trick to force it to have 100% height. This involves setting both the top and bottom property to 0, which is in effect is telling the element to be at both locations, resulting in a full-height element. This also works for the left and right properties to create a full-width element. We must position our first portfolio-item away from the top edge of the window which we achieve here by setting the top property to 30px.

The portfolio-info articles should, again, be 100% wide unless we are at $break-three or above. To create the small red circle seen on the design I decided to use the date elements, as the two seem linked. In the CSS the best way to achieve it is to use a :before pseudo element. To make it a circle it has to have some height and width and a border-radius of 50%. We then absolutely position it off to the left of the date. To get the small gap around it, simply apply a border the same colour as the portfolio-item elements.

The meta information is very simple. Just set the text black and give it a top margin. Any <p> tags inside here should have no margins. Save the file and look at what this relatively short section of code has achieved.

This is getting very close now! Let's carry on with our content styles...

.portfolio-image {
    padding: $padding*2;
    background: $white;
    border-left: 1px solid darken($grey, 10%);
    text-align: center;

    @media screen and (min-width: $break-three) {
        float: left;
        width: 70%;
    }

    img {
        width: 100%;
        max-width: 610px;
        height: auto;
    }

    div.loading {
        img {
            width: auto;
            height: auto;
        }
    }
}

Place this directly after the portfolio-info styles. This short block of code will make the images inside the portfolio-items responsive. The idea is to scale down the images to as small as we need, but only scale up to their actual width. This means you have to have fixed-width images, but it's quite feasible in a layout/template like this, as the images would probably be dynamically generated and cropped to a certain size.

We're also using the portfolio-image elements to add another design detail, that being the thin border between the images and the information. If you take another look in the browser now, you should see the images nicely scaling with the browser and just generally looking awesome!

Next up

It's time for a coffee break! In the following, and final, part of this series, we'll add some flare to the layout. We'll build the loading icon at the bottom of our content area, animating it with CSS3, then mimicking infinite scrolling as more portfolio items are loaded in.

Advertisement